xref: /third_party/rust/crates/rustix/src/path/arg.rs (revision b8a62b91)
1//! Convenient and efficient string argument passing.
2//!
3//! This module defines the `Arg` trait and implements it for several common
4//! string types. This allows users to pass any of these string types directly
5//! to rustix APIs with string arguments, and it allows rustix to implement
6//! NUL-termination without the need for copying where possible.
7
8use crate::ffi::{CStr, CString};
9use crate::io;
10#[cfg(feature = "itoa")]
11use crate::path::DecInt;
12use crate::path::SMALL_PATH_BUFFER_SIZE;
13use alloc::borrow::Cow;
14#[cfg(feature = "itoa")]
15use alloc::borrow::ToOwned;
16use alloc::string::String;
17use alloc::vec::Vec;
18use core::mem::MaybeUninit;
19use core::{ptr, slice, str};
20#[cfg(feature = "std")]
21use std::ffi::{OsStr, OsString};
22#[cfg(feature = "std")]
23#[cfg(target_os = "hermit")]
24use std::os::hermit::ext::ffi::{OsStrExt, OsStringExt};
25#[cfg(feature = "std")]
26#[cfg(unix)]
27use std::os::unix::ffi::{OsStrExt, OsStringExt};
28#[cfg(feature = "std")]
29#[cfg(target_os = "vxworks")]
30use std::os::vxworks::ext::ffi::{OsStrExt, OsStringExt};
31#[cfg(feature = "std")]
32#[cfg(target_os = "wasi")]
33use std::os::wasi::ffi::{OsStrExt, OsStringExt};
34#[cfg(feature = "std")]
35use std::path::{Component, Components, Iter, Path, PathBuf};
36
37/// A trait for passing path arguments.
38///
39/// This is similar to [`AsRef`]`<`[`Path`]`>`, but is implemented for more
40/// kinds of strings and can convert into more kinds of strings.
41///
42/// # Example
43///
44/// ```rust
45/// # #[cfg(any(feature = "fs", feature = "net"))]
46/// use rustix::ffi::CStr;
47/// use rustix::io;
48/// # #[cfg(any(feature = "fs", feature = "net"))]
49/// use rustix::path::Arg;
50///
51/// # #[cfg(any(feature = "fs", feature = "net"))]
52/// pub fn touch<P: Arg>(path: P) -> io::Result<()> {
53///     let path = path.into_c_str()?;
54///     _touch(&path)
55/// }
56///
57/// # #[cfg(any(feature = "fs", feature = "net"))]
58/// fn _touch(path: &CStr) -> io::Result<()> {
59///     // implementation goes here
60///     Ok(())
61/// }
62/// ```
63///
64/// Users can then call `touch("foo")`, `touch(cstr!("foo"))`,
65/// `touch(Path::new("foo"))`, or many other things.
66///
67/// [`AsRef`]: std::convert::AsRef
68pub trait Arg {
69    /// Returns a view of this string as a string slice.
70    fn as_str(&self) -> io::Result<&str>;
71
72    /// Returns a potentially-lossy rendering of this string as a `Cow<'_,
73    /// str>`.
74    fn to_string_lossy(&self) -> Cow<'_, str>;
75
76    /// Returns a view of this string as a maybe-owned [`CStr`].
77    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>>;
78
79    /// Consumes `self` and returns a view of this string as a maybe-owned
80    /// [`CStr`].
81    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
82    where
83        Self: 'b;
84
85    /// Runs a closure with `self` passed in as a `&CStr`.
86    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
87    where
88        Self: Sized,
89        F: FnOnce(&CStr) -> io::Result<T>;
90}
91
92impl Arg for &str {
93    #[inline]
94    fn as_str(&self) -> io::Result<&str> {
95        Ok(self)
96    }
97
98    #[inline]
99    fn to_string_lossy(&self) -> Cow<'_, str> {
100        Cow::Borrowed(self)
101    }
102
103    #[inline]
104    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
105        Ok(Cow::Owned(
106            CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?,
107        ))
108    }
109
110    #[inline]
111    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
112    where
113        Self: 'b,
114    {
115        Ok(Cow::Owned(
116            CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?,
117        ))
118    }
119
120    #[inline]
121    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
122    where
123        Self: Sized,
124        F: FnOnce(&CStr) -> io::Result<T>,
125    {
126        with_c_str(self.as_bytes(), f)
127    }
128}
129
130impl Arg for &String {
131    #[inline]
132    fn as_str(&self) -> io::Result<&str> {
133        Ok(self)
134    }
135
136    #[inline]
137    fn to_string_lossy(&self) -> Cow<'_, str> {
138        Cow::Borrowed(self)
139    }
140
141    #[inline]
142    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
143        Ok(Cow::Owned(
144            CString::new(String::as_str(self)).map_err(|_cstr_err| io::Errno::INVAL)?,
145        ))
146    }
147
148    #[inline]
149    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
150    where
151        Self: 'b,
152    {
153        self.as_str().into_c_str()
154    }
155
156    #[inline]
157    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
158    where
159        Self: Sized,
160        F: FnOnce(&CStr) -> io::Result<T>,
161    {
162        with_c_str(self.as_bytes(), f)
163    }
164}
165
166impl Arg for String {
167    #[inline]
168    fn as_str(&self) -> io::Result<&str> {
169        Ok(self)
170    }
171
172    #[inline]
173    fn to_string_lossy(&self) -> Cow<'_, str> {
174        Cow::Borrowed(self)
175    }
176
177    #[inline]
178    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
179        Ok(Cow::Owned(
180            CString::new(self.as_str()).map_err(|_cstr_err| io::Errno::INVAL)?,
181        ))
182    }
183
184    #[inline]
185    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
186    where
187        Self: 'b,
188    {
189        Ok(Cow::Owned(
190            CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?,
191        ))
192    }
193
194    #[inline]
195    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
196    where
197        Self: Sized,
198        F: FnOnce(&CStr) -> io::Result<T>,
199    {
200        f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?)
201    }
202}
203
204#[cfg(feature = "std")]
205impl Arg for &OsStr {
206    #[inline]
207    fn as_str(&self) -> io::Result<&str> {
208        self.to_str().ok_or(io::Errno::INVAL)
209    }
210
211    #[inline]
212    fn to_string_lossy(&self) -> Cow<'_, str> {
213        OsStr::to_string_lossy(self)
214    }
215
216    #[inline]
217    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
218        Ok(Cow::Owned(
219            CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?,
220        ))
221    }
222
223    #[inline]
224    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
225    where
226        Self: 'b,
227    {
228        Ok(Cow::Owned(
229            CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?,
230        ))
231    }
232
233    #[inline]
234    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
235    where
236        Self: Sized,
237        F: FnOnce(&CStr) -> io::Result<T>,
238    {
239        with_c_str(self.as_bytes(), f)
240    }
241}
242
243#[cfg(feature = "std")]
244impl Arg for &OsString {
245    #[inline]
246    fn as_str(&self) -> io::Result<&str> {
247        OsString::as_os_str(self).to_str().ok_or(io::Errno::INVAL)
248    }
249
250    #[inline]
251    fn to_string_lossy(&self) -> Cow<'_, str> {
252        self.as_os_str().to_string_lossy()
253    }
254
255    #[inline]
256    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
257        Ok(Cow::Owned(
258            CString::new(OsString::as_os_str(self).as_bytes())
259                .map_err(|_cstr_err| io::Errno::INVAL)?,
260        ))
261    }
262
263    #[inline]
264    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
265    where
266        Self: 'b,
267    {
268        self.as_os_str().into_c_str()
269    }
270
271    #[inline]
272    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
273    where
274        Self: Sized,
275        F: FnOnce(&CStr) -> io::Result<T>,
276    {
277        with_c_str(self.as_bytes(), f)
278    }
279}
280
281#[cfg(feature = "std")]
282impl Arg for OsString {
283    #[inline]
284    fn as_str(&self) -> io::Result<&str> {
285        self.as_os_str().to_str().ok_or(io::Errno::INVAL)
286    }
287
288    #[inline]
289    fn to_string_lossy(&self) -> Cow<'_, str> {
290        self.as_os_str().to_string_lossy()
291    }
292
293    #[inline]
294    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
295        Ok(Cow::Owned(
296            CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?,
297        ))
298    }
299
300    #[inline]
301    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
302    where
303        Self: 'b,
304    {
305        Ok(Cow::Owned(
306            CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?,
307        ))
308    }
309
310    #[inline]
311    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
312    where
313        Self: Sized,
314        F: FnOnce(&CStr) -> io::Result<T>,
315    {
316        f(&CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?)
317    }
318}
319
320#[cfg(feature = "std")]
321impl Arg for &Path {
322    #[inline]
323    fn as_str(&self) -> io::Result<&str> {
324        self.as_os_str().to_str().ok_or(io::Errno::INVAL)
325    }
326
327    #[inline]
328    fn to_string_lossy(&self) -> Cow<'_, str> {
329        Path::to_string_lossy(self)
330    }
331
332    #[inline]
333    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
334        Ok(Cow::Owned(
335            CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?,
336        ))
337    }
338
339    #[inline]
340    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
341    where
342        Self: 'b,
343    {
344        Ok(Cow::Owned(
345            CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?,
346        ))
347    }
348
349    #[inline]
350    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
351    where
352        Self: Sized,
353        F: FnOnce(&CStr) -> io::Result<T>,
354    {
355        with_c_str(self.as_os_str().as_bytes(), f)
356    }
357}
358
359#[cfg(feature = "std")]
360impl Arg for &PathBuf {
361    #[inline]
362    fn as_str(&self) -> io::Result<&str> {
363        PathBuf::as_path(self)
364            .as_os_str()
365            .to_str()
366            .ok_or(io::Errno::INVAL)
367    }
368
369    #[inline]
370    fn to_string_lossy(&self) -> Cow<'_, str> {
371        self.as_path().to_string_lossy()
372    }
373
374    #[inline]
375    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
376        Ok(Cow::Owned(
377            CString::new(PathBuf::as_path(self).as_os_str().as_bytes())
378                .map_err(|_cstr_err| io::Errno::INVAL)?,
379        ))
380    }
381
382    #[inline]
383    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
384    where
385        Self: 'b,
386    {
387        self.as_path().into_c_str()
388    }
389
390    #[inline]
391    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
392    where
393        Self: Sized,
394        F: FnOnce(&CStr) -> io::Result<T>,
395    {
396        with_c_str(self.as_os_str().as_bytes(), f)
397    }
398}
399
400#[cfg(feature = "std")]
401impl Arg for PathBuf {
402    #[inline]
403    fn as_str(&self) -> io::Result<&str> {
404        self.as_os_str().to_str().ok_or(io::Errno::INVAL)
405    }
406
407    #[inline]
408    fn to_string_lossy(&self) -> Cow<'_, str> {
409        self.as_os_str().to_string_lossy()
410    }
411
412    #[inline]
413    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
414        Ok(Cow::Owned(
415            CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?,
416        ))
417    }
418
419    #[inline]
420    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
421    where
422        Self: 'b,
423    {
424        Ok(Cow::Owned(
425            CString::new(self.into_os_string().into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?,
426        ))
427    }
428
429    #[inline]
430    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
431    where
432        Self: Sized,
433        F: FnOnce(&CStr) -> io::Result<T>,
434    {
435        f(
436            &CString::new(self.into_os_string().into_vec())
437                .map_err(|_cstr_err| io::Errno::INVAL)?,
438        )
439    }
440}
441
442impl Arg for &CStr {
443    #[inline]
444    fn as_str(&self) -> io::Result<&str> {
445        self.to_str().map_err(|_utf8_err| io::Errno::INVAL)
446    }
447
448    #[inline]
449    fn to_string_lossy(&self) -> Cow<'_, str> {
450        CStr::to_string_lossy(self)
451    }
452
453    #[inline]
454    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
455        Ok(Cow::Borrowed(self))
456    }
457
458    #[inline]
459    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
460    where
461        Self: 'b,
462    {
463        Ok(Cow::Borrowed(self))
464    }
465
466    #[inline]
467    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
468    where
469        Self: Sized,
470        F: FnOnce(&CStr) -> io::Result<T>,
471    {
472        f(self)
473    }
474}
475
476impl Arg for &CString {
477    #[inline]
478    fn as_str(&self) -> io::Result<&str> {
479        unimplemented!()
480    }
481
482    #[inline]
483    fn to_string_lossy(&self) -> Cow<'_, str> {
484        unimplemented!()
485    }
486
487    #[inline]
488    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
489        Ok(Cow::Borrowed(self))
490    }
491
492    #[inline]
493    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
494    where
495        Self: 'b,
496    {
497        Ok(Cow::Borrowed(self))
498    }
499
500    #[inline]
501    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
502    where
503        Self: Sized,
504        F: FnOnce(&CStr) -> io::Result<T>,
505    {
506        f(self)
507    }
508}
509
510impl Arg for CString {
511    #[inline]
512    fn as_str(&self) -> io::Result<&str> {
513        self.to_str().map_err(|_utf8_err| io::Errno::INVAL)
514    }
515
516    #[inline]
517    fn to_string_lossy(&self) -> Cow<'_, str> {
518        CStr::to_string_lossy(self)
519    }
520
521    #[inline]
522    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
523        Ok(Cow::Borrowed(self))
524    }
525
526    #[inline]
527    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
528    where
529        Self: 'b,
530    {
531        Ok(Cow::Owned(self))
532    }
533
534    #[inline]
535    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
536    where
537        Self: Sized,
538        F: FnOnce(&CStr) -> io::Result<T>,
539    {
540        f(&self)
541    }
542}
543
544impl<'a> Arg for Cow<'a, str> {
545    #[inline]
546    fn as_str(&self) -> io::Result<&str> {
547        Ok(self)
548    }
549
550    #[inline]
551    fn to_string_lossy(&self) -> Cow<'_, str> {
552        Cow::Borrowed(self)
553    }
554
555    #[inline]
556    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
557        Ok(Cow::Owned(
558            CString::new(self.as_ref()).map_err(|_cstr_err| io::Errno::INVAL)?,
559        ))
560    }
561
562    #[inline]
563    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
564    where
565        Self: 'b,
566    {
567        Ok(Cow::Owned(
568            match self {
569                Cow::Owned(s) => CString::new(s),
570                Cow::Borrowed(s) => CString::new(s),
571            }
572            .map_err(|_cstr_err| io::Errno::INVAL)?,
573        ))
574    }
575
576    #[inline]
577    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
578    where
579        Self: Sized,
580        F: FnOnce(&CStr) -> io::Result<T>,
581    {
582        with_c_str(self.as_bytes(), f)
583    }
584}
585
586#[cfg(feature = "std")]
587impl<'a> Arg for Cow<'a, OsStr> {
588    #[inline]
589    fn as_str(&self) -> io::Result<&str> {
590        (**self).to_str().ok_or(io::Errno::INVAL)
591    }
592
593    #[inline]
594    fn to_string_lossy(&self) -> Cow<'_, str> {
595        (**self).to_string_lossy()
596    }
597
598    #[inline]
599    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
600        Ok(Cow::Owned(
601            CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?,
602        ))
603    }
604
605    #[inline]
606    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
607    where
608        Self: 'b,
609    {
610        Ok(Cow::Owned(
611            match self {
612                Cow::Owned(os) => CString::new(os.into_vec()),
613                Cow::Borrowed(os) => CString::new(os.as_bytes()),
614            }
615            .map_err(|_cstr_err| io::Errno::INVAL)?,
616        ))
617    }
618
619    #[inline]
620    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
621    where
622        Self: Sized,
623        F: FnOnce(&CStr) -> io::Result<T>,
624    {
625        with_c_str(self.as_bytes(), f)
626    }
627}
628
629impl<'a> Arg for Cow<'a, CStr> {
630    #[inline]
631    fn as_str(&self) -> io::Result<&str> {
632        self.to_str().map_err(|_utf8_err| io::Errno::INVAL)
633    }
634
635    #[inline]
636    fn to_string_lossy(&self) -> Cow<'_, str> {
637        let borrow: &CStr = core::borrow::Borrow::borrow(self);
638        borrow.to_string_lossy()
639    }
640
641    #[inline]
642    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
643        Ok(Cow::Borrowed(self))
644    }
645
646    #[inline]
647    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
648    where
649        Self: 'b,
650    {
651        Ok(self)
652    }
653
654    #[inline]
655    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
656    where
657        Self: Sized,
658        F: FnOnce(&CStr) -> io::Result<T>,
659    {
660        f(&self)
661    }
662}
663
664#[cfg(feature = "std")]
665impl<'a> Arg for Component<'a> {
666    #[inline]
667    fn as_str(&self) -> io::Result<&str> {
668        self.as_os_str().to_str().ok_or(io::Errno::INVAL)
669    }
670
671    #[inline]
672    fn to_string_lossy(&self) -> Cow<'_, str> {
673        self.as_os_str().to_string_lossy()
674    }
675
676    #[inline]
677    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
678        Ok(Cow::Owned(
679            CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?,
680        ))
681    }
682
683    #[inline]
684    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
685    where
686        Self: 'b,
687    {
688        Ok(Cow::Owned(
689            CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?,
690        ))
691    }
692
693    #[inline]
694    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
695    where
696        Self: Sized,
697        F: FnOnce(&CStr) -> io::Result<T>,
698    {
699        with_c_str(self.as_os_str().as_bytes(), f)
700    }
701}
702
703#[cfg(feature = "std")]
704impl<'a> Arg for Components<'a> {
705    #[inline]
706    fn as_str(&self) -> io::Result<&str> {
707        self.as_path().to_str().ok_or(io::Errno::INVAL)
708    }
709
710    #[inline]
711    fn to_string_lossy(&self) -> Cow<'_, str> {
712        self.as_path().to_string_lossy()
713    }
714
715    #[inline]
716    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
717        Ok(Cow::Owned(
718            CString::new(self.as_path().as_os_str().as_bytes())
719                .map_err(|_cstr_err| io::Errno::INVAL)?,
720        ))
721    }
722
723    #[inline]
724    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
725    where
726        Self: 'b,
727    {
728        Ok(Cow::Owned(
729            CString::new(self.as_path().as_os_str().as_bytes())
730                .map_err(|_cstr_err| io::Errno::INVAL)?,
731        ))
732    }
733
734    #[inline]
735    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
736    where
737        Self: Sized,
738        F: FnOnce(&CStr) -> io::Result<T>,
739    {
740        with_c_str(self.as_path().as_os_str().as_bytes(), f)
741    }
742}
743
744#[cfg(feature = "std")]
745impl<'a> Arg for Iter<'a> {
746    #[inline]
747    fn as_str(&self) -> io::Result<&str> {
748        self.as_path().to_str().ok_or(io::Errno::INVAL)
749    }
750
751    #[inline]
752    fn to_string_lossy(&self) -> Cow<'_, str> {
753        self.as_path().to_string_lossy()
754    }
755
756    #[inline]
757    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
758        Ok(Cow::Owned(
759            CString::new(self.as_path().as_os_str().as_bytes())
760                .map_err(|_cstr_err| io::Errno::INVAL)?,
761        ))
762    }
763
764    #[inline]
765    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
766    where
767        Self: 'b,
768    {
769        Ok(Cow::Owned(
770            CString::new(self.as_path().as_os_str().as_bytes())
771                .map_err(|_cstr_err| io::Errno::INVAL)?,
772        ))
773    }
774
775    #[inline]
776    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
777    where
778        Self: Sized,
779        F: FnOnce(&CStr) -> io::Result<T>,
780    {
781        with_c_str(self.as_path().as_os_str().as_bytes(), f)
782    }
783}
784
785impl Arg for &[u8] {
786    #[inline]
787    fn as_str(&self) -> io::Result<&str> {
788        str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL)
789    }
790
791    #[inline]
792    fn to_string_lossy(&self) -> Cow<'_, str> {
793        String::from_utf8_lossy(self)
794    }
795
796    #[inline]
797    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
798        Ok(Cow::Owned(
799            CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?,
800        ))
801    }
802
803    #[inline]
804    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
805    where
806        Self: 'b,
807    {
808        Ok(Cow::Owned(
809            CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?,
810        ))
811    }
812
813    #[inline]
814    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
815    where
816        Self: Sized,
817        F: FnOnce(&CStr) -> io::Result<T>,
818    {
819        with_c_str(self, f)
820    }
821}
822
823impl Arg for &Vec<u8> {
824    #[inline]
825    fn as_str(&self) -> io::Result<&str> {
826        str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL)
827    }
828
829    #[inline]
830    fn to_string_lossy(&self) -> Cow<'_, str> {
831        String::from_utf8_lossy(self)
832    }
833
834    #[inline]
835    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
836        Ok(Cow::Owned(
837            CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?,
838        ))
839    }
840
841    #[inline]
842    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
843    where
844        Self: 'b,
845    {
846        Ok(Cow::Owned(
847            CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?,
848        ))
849    }
850
851    #[inline]
852    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
853    where
854        Self: Sized,
855        F: FnOnce(&CStr) -> io::Result<T>,
856    {
857        with_c_str(self, f)
858    }
859}
860
861impl Arg for Vec<u8> {
862    #[inline]
863    fn as_str(&self) -> io::Result<&str> {
864        str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL)
865    }
866
867    #[inline]
868    fn to_string_lossy(&self) -> Cow<'_, str> {
869        String::from_utf8_lossy(self)
870    }
871
872    #[inline]
873    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
874        Ok(Cow::Owned(
875            CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?,
876        ))
877    }
878
879    #[inline]
880    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
881    where
882        Self: 'b,
883    {
884        Ok(Cow::Owned(
885            CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?,
886        ))
887    }
888
889    #[inline]
890    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
891    where
892        Self: Sized,
893        F: FnOnce(&CStr) -> io::Result<T>,
894    {
895        f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?)
896    }
897}
898
899#[cfg(feature = "itoa")]
900impl Arg for DecInt {
901    #[inline]
902    fn as_str(&self) -> io::Result<&str> {
903        Ok(self.as_str())
904    }
905
906    #[inline]
907    fn to_string_lossy(&self) -> Cow<'_, str> {
908        Cow::Borrowed(self.as_str())
909    }
910
911    #[inline]
912    fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> {
913        Ok(Cow::Borrowed(self.as_c_str()))
914    }
915
916    #[inline]
917    fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>>
918    where
919        Self: 'b,
920    {
921        Ok(Cow::Owned(self.as_c_str().to_owned()))
922    }
923
924    #[inline]
925    fn into_with_c_str<T, F>(self, f: F) -> io::Result<T>
926    where
927        Self: Sized,
928        F: FnOnce(&CStr) -> io::Result<T>,
929    {
930        f(self.as_c_str())
931    }
932}
933
934/// Runs a closure with `bytes` passed in as a `&CStr`.
935#[allow(unsafe_code, clippy::int_plus_one)]
936#[inline]
937fn with_c_str<T, F>(bytes: &[u8], f: F) -> io::Result<T>
938where
939    F: FnOnce(&CStr) -> io::Result<T>,
940{
941    // Most paths are less than `SMALL_PATH_BUFFER_SIZE` long. The rest can go
942    // through the dynamic allocation path. If you're opening many files in a
943    // directory with a long path, consider opening the directory and using
944    // `openat` to open the files under it, which will avoid this, and is often
945    // faster in the OS as well.
946
947    // Test with >= so that we have room for the trailing NUL.
948    if bytes.len() >= SMALL_PATH_BUFFER_SIZE {
949        return with_c_str_slow_path(bytes, f);
950    }
951
952    // Taken from
953    // https://github.com/rust-lang/rust/blob/a00f8ba7fcac1b27341679c51bf5a3271fa82df3/library/std/src/sys/common/small_c_string.rs
954    let mut buf = MaybeUninit::<[u8; SMALL_PATH_BUFFER_SIZE]>::uninit();
955    let buf_ptr = buf.as_mut_ptr() as *mut u8;
956
957    // SAFETY: bytes.len() < SMALL_PATH_BUFFER_SIZE which means we have space for
958    // bytes.len() + 1 u8s:
959    debug_assert!(bytes.len() + 1 <= SMALL_PATH_BUFFER_SIZE);
960    unsafe {
961        ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len());
962        buf_ptr.add(bytes.len()).write(0);
963    }
964
965    // SAFETY: we just wrote the bytes above and they will remain valid for the
966    // duration of f b/c buf doesn't get dropped until the end of the function.
967    match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) {
968        Ok(s) => f(s),
969        Err(_) => Err(io::Errno::INVAL),
970    }
971}
972
973/// The slow path which handles any length. In theory OS's only support up
974/// to `PATH_MAX`, but we let the OS enforce that.
975#[cold]
976fn with_c_str_slow_path<T, F>(bytes: &[u8], f: F) -> io::Result<T>
977where
978    F: FnOnce(&CStr) -> io::Result<T>,
979{
980    f(&CString::new(bytes).map_err(|_cstr_err| io::Errno::INVAL)?)
981}
982