ftzz/utils/
fast_path.rs

1use std::{
2    ffi::OsStr,
3    fmt,
4    ops::{Deref, DerefMut},
5    path::{Path, PathBuf, MAIN_SEPARATOR},
6};
7
8/// A specialized [`PathBuf`][std::path::PathBuf] implementation that takes
9/// advantage of a few assumptions. Specifically, it *only* supports adding
10/// single-level directories (e.g. "foo", "foo/bar" is not allowed) and updating
11/// the current file name.
12pub struct FastPathBuf {
13    inner: Vec<u8>,
14    last_len: usize,
15}
16
17impl FastPathBuf {
18    #[must_use]
19    pub fn new() -> Self {
20        Self::with_capacity(0)
21    }
22
23    #[must_use]
24    pub fn with_capacity(capacity: usize) -> Self {
25        Self {
26            inner: Vec::with_capacity(capacity),
27            last_len: 0,
28        }
29    }
30
31    pub fn capacity(&self) -> usize {
32        self.inner.capacity()
33    }
34
35    pub fn reserve(&mut self, additional: usize) {
36        self.inner.reserve(additional);
37    }
38
39    #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
40    pub fn push(&mut self, name: &str) -> PopGuard {
41        PopGuard::push(self, name)
42    }
43
44    #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
45    pub unsafe fn pop(&mut self) {
46        let Self {
47            ref mut inner,
48            last_len,
49        } = *self;
50
51        if inner.len() > last_len {
52            inner.truncate(last_len);
53        } else {
54            self.inner.truncate({
55                let parent = self.parent();
56                let parent = unsafe { parent.unwrap_unchecked() };
57                parent.as_os_str().len()
58            });
59        }
60    }
61
62    #[cfg_attr(feature = "tracing", tracing::instrument(level = "trace"))]
63    pub unsafe fn set_file_name(&mut self, name: &str) {
64        unsafe {
65            self.pop();
66        }
67        self.push(name);
68    }
69
70    #[cfg(all(unix, not(miri)))]
71    pub fn to_cstr_mut(&mut self) -> unix::CStrFastPathBufGuard {
72        unix::CStrFastPathBufGuard::new(self)
73    }
74}
75
76impl From<PathBuf> for FastPathBuf {
77    fn from(p: PathBuf) -> Self {
78        let inner = p.into_os_string().into_encoded_bytes();
79        Self {
80            last_len: inner.len(),
81            inner,
82        }
83    }
84}
85
86impl Default for FastPathBuf {
87    fn default() -> Self {
88        Self::new()
89    }
90}
91
92impl Deref for FastPathBuf {
93    type Target = Path;
94
95    fn deref(&self) -> &Self::Target {
96        let Self {
97            ref inner,
98            last_len: _,
99        } = *self;
100
101        unsafe { OsStr::from_encoded_bytes_unchecked(inner) }.as_ref()
102    }
103}
104
105impl AsRef<Path> for FastPathBuf {
106    fn as_ref(&self) -> &Path {
107        self
108    }
109}
110
111impl fmt::Debug for FastPathBuf {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        fmt::Debug::fmt(&**self, f)
114    }
115}
116
117impl Clone for FastPathBuf {
118    fn clone(&self) -> Self {
119        let Self {
120            ref inner,
121            last_len,
122        } = *self;
123
124        Self {
125            inner: inner.clone(),
126            last_len,
127        }
128    }
129
130    fn clone_from(&mut self, source: &Self) {
131        let Self {
132            ref mut inner,
133            ref mut last_len,
134        } = *self;
135
136        inner.clone_from(&source.inner);
137        *last_len = source.last_len;
138    }
139}
140
141pub struct PopGuard<'a>(&'a mut FastPathBuf);
142
143impl<'a> PopGuard<'a> {
144    fn push(path: &'a mut FastPathBuf, name: &str) -> Self {
145        let FastPathBuf {
146            ref mut inner,
147            ref mut last_len,
148        } = *path;
149
150        *last_len = inner.len();
151
152        // Reserve an extra byte for the eventually appended NUL terminator
153        inner.reserve(1 + name.len() + 1);
154        inner.push(MAIN_SEPARATOR as u8);
155        inner.extend_from_slice(name.as_bytes());
156
157        Self(path)
158    }
159
160    pub fn pop(self) {
161        unsafe { self.0.pop() }
162    }
163}
164
165impl Deref for PopGuard<'_> {
166    type Target = FastPathBuf;
167
168    fn deref(&self) -> &Self::Target {
169        self.0
170    }
171}
172
173impl DerefMut for PopGuard<'_> {
174    fn deref_mut(&mut self) -> &mut Self::Target {
175        self.0
176    }
177}
178
179impl AsRef<Path> for PopGuard<'_> {
180    fn as_ref(&self) -> &Path {
181        self.0
182    }
183}
184
185impl fmt::Debug for PopGuard<'_> {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        fmt::Debug::fmt(&**self, f)
188    }
189}
190
191#[cfg(all(unix, not(miri)))]
192mod unix {
193    use std::{ffi::CStr, ops::Deref};
194
195    use super::FastPathBuf;
196
197    #[must_use]
198    pub struct CStrFastPathBufGuard<'a> {
199        buf: &'a mut FastPathBuf,
200    }
201
202    impl<'a> CStrFastPathBufGuard<'a> {
203        pub fn new(buf: &mut FastPathBuf) -> CStrFastPathBufGuard {
204            let FastPathBuf {
205                ref mut inner,
206                last_len: _,
207            } = *buf;
208
209            inner.push(0); // NUL terminator
210            CStrFastPathBufGuard { buf }
211        }
212    }
213
214    impl<'a> Deref for CStrFastPathBufGuard<'a> {
215        type Target = CStr;
216
217        fn deref(&self) -> &Self::Target {
218            let Self {
219                buf:
220                    FastPathBuf {
221                        ref inner,
222                        last_len: _,
223                    },
224            } = *self;
225
226            if cfg!(debug_assertions) {
227                CStr::from_bytes_with_nul(inner).unwrap()
228            } else {
229                unsafe { CStr::from_bytes_with_nul_unchecked(inner) }
230            }
231        }
232    }
233
234    impl<'a> AsRef<CStr> for CStrFastPathBufGuard<'a> {
235        fn as_ref(&self) -> &CStr {
236            self
237        }
238    }
239
240    impl<'a> Drop for CStrFastPathBufGuard<'a> {
241        fn drop(&mut self) {
242            let Self {
243                buf:
244                    FastPathBuf {
245                        ref mut inner,
246                        last_len: _,
247                    },
248            } = *self;
249
250            inner.pop();
251        }
252    }
253}