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 
8 use crate::ffi::{CStr, CString};
9 use crate::io;
10 #[cfg(feature = "itoa")]
11 use crate::path::DecInt;
12 use crate::path::SMALL_PATH_BUFFER_SIZE;
13 use alloc::borrow::Cow;
14 #[cfg(feature = "itoa")]
15 use alloc::borrow::ToOwned;
16 use alloc::string::String;
17 use alloc::vec::Vec;
18 use core::mem::MaybeUninit;
19 use core::{ptr, slice, str};
20 #[cfg(feature = "std")]
21 use std::ffi::{OsStr, OsString};
22 #[cfg(feature = "std")]
23 #[cfg(target_os = "hermit")]
24 use std::os::hermit::ext::ffi::{OsStrExt, OsStringExt};
25 #[cfg(feature = "std")]
26 #[cfg(unix)]
27 use std::os::unix::ffi::{OsStrExt, OsStringExt};
28 #[cfg(feature = "std")]
29 #[cfg(target_os = "vxworks")]
30 use std::os::vxworks::ext::ffi::{OsStrExt, OsStringExt};
31 #[cfg(feature = "std")]
32 #[cfg(target_os = "wasi")]
33 use std::os::wasi::ffi::{OsStrExt, OsStringExt};
34 #[cfg(feature = "std")]
35 use 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
68 pub trait Arg {
69     /// Returns a view of this string as a string slice.
as_strnull70     fn as_str(&self) -> io::Result<&str>;
71 
72     /// Returns a potentially-lossy rendering of this string as a `Cow<'_,
73     /// str>`.
to_string_lossynull74     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 
92 impl Arg for &str {
93     #[inline]
as_strnull94     fn as_str(&self) -> io::Result<&str> {
95         Ok(self)
96     }
97 
98     #[inline]
to_string_lossynull99     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]
into_with_c_strnull121     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 
130 impl Arg for &String {
131     #[inline]
as_strnull132     fn as_str(&self) -> io::Result<&str> {
133         Ok(self)
134     }
135 
136     #[inline]
to_string_lossynull137     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]
into_with_c_strnull157     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 
166 impl Arg for String {
167     #[inline]
as_strnull168     fn as_str(&self) -> io::Result<&str> {
169         Ok(self)
170     }
171 
172     #[inline]
to_string_lossynull173     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]
into_with_c_strnull195     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")]
205 impl Arg for &OsStr {
206     #[inline]
as_strnull207     fn as_str(&self) -> io::Result<&str> {
208         self.to_str().ok_or(io::Errno::INVAL)
209     }
210 
211     #[inline]
to_string_lossynull212     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]
into_with_c_strnull234     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")]
244 impl Arg for &OsString {
245     #[inline]
as_strnull246     fn as_str(&self) -> io::Result<&str> {
247         OsString::as_os_str(self).to_str().ok_or(io::Errno::INVAL)
248     }
249 
250     #[inline]
to_string_lossynull251     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]
into_with_c_strnull272     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")]
282 impl Arg for OsString {
283     #[inline]
as_strnull284     fn as_str(&self) -> io::Result<&str> {
285         self.as_os_str().to_str().ok_or(io::Errno::INVAL)
286     }
287 
288     #[inline]
to_string_lossynull289     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]
into_with_c_strnull311     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")]
321 impl Arg for &Path {
322     #[inline]
as_strnull323     fn as_str(&self) -> io::Result<&str> {
324         self.as_os_str().to_str().ok_or(io::Errno::INVAL)
325     }
326 
327     #[inline]
to_string_lossynull328     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]
into_with_c_strnull350     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")]
360 impl Arg for &PathBuf {
361     #[inline]
as_strnull362     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]
to_string_lossynull370     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]
into_with_c_strnull391     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")]
401 impl Arg for PathBuf {
402     #[inline]
as_strnull403     fn as_str(&self) -> io::Result<&str> {
404         self.as_os_str().to_str().ok_or(io::Errno::INVAL)
405     }
406 
407     #[inline]
to_string_lossynull408     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]
into_with_c_strnull430     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 
442 impl Arg for &CStr {
443     #[inline]
as_strnull444     fn as_str(&self) -> io::Result<&str> {
445         self.to_str().map_err(|_utf8_err| io::Errno::INVAL)
446     }
447 
448     #[inline]
to_string_lossynull449     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]
into_with_c_strnull467     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 
476 impl Arg for &CString {
477     #[inline]
as_strnull478     fn as_str(&self) -> io::Result<&str> {
479         unimplemented!()
480     }
481 
482     #[inline]
to_string_lossynull483     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]
into_with_c_strnull501     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 
510 impl Arg for CString {
511     #[inline]
as_strnull512     fn as_str(&self) -> io::Result<&str> {
513         self.to_str().map_err(|_utf8_err| io::Errno::INVAL)
514     }
515 
516     #[inline]
to_string_lossynull517     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]
into_with_c_strnull535     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 
544 impl<'a> Arg for Cow<'a, str> {
545     #[inline]
as_strnull546     fn as_str(&self) -> io::Result<&str> {
547         Ok(self)
548     }
549 
550     #[inline]
to_string_lossynull551     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]
into_with_c_strnull577     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")]
587 impl<'a> Arg for Cow<'a, OsStr> {
588     #[inline]
as_strnull589     fn as_str(&self) -> io::Result<&str> {
590         (**self).to_str().ok_or(io::Errno::INVAL)
591     }
592 
593     #[inline]
to_string_lossynull594     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]
into_with_c_strnull620     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 
629 impl<'a> Arg for Cow<'a, CStr> {
630     #[inline]
as_strnull631     fn as_str(&self) -> io::Result<&str> {
632         self.to_str().map_err(|_utf8_err| io::Errno::INVAL)
633     }
634 
635     #[inline]
to_string_lossynull636     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]
as_cow_c_strnull642     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]
into_with_c_strnull655     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")]
665 impl<'a> Arg for Component<'a> {
666     #[inline]
as_strnull667     fn as_str(&self) -> io::Result<&str> {
668         self.as_os_str().to_str().ok_or(io::Errno::INVAL)
669     }
670 
671     #[inline]
to_string_lossynull672     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]
into_with_c_strnull694     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")]
704 impl<'a> Arg for Components<'a> {
705     #[inline]
as_strnull706     fn as_str(&self) -> io::Result<&str> {
707         self.as_path().to_str().ok_or(io::Errno::INVAL)
708     }
709 
710     #[inline]
to_string_lossynull711     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]
into_with_c_strnull735     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")]
745 impl<'a> Arg for Iter<'a> {
746     #[inline]
as_strnull747     fn as_str(&self) -> io::Result<&str> {
748         self.as_path().to_str().ok_or(io::Errno::INVAL)
749     }
750 
751     #[inline]
to_string_lossynull752     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]
into_with_c_strnull776     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 
785 impl Arg for &[u8] {
786     #[inline]
as_strnull787     fn as_str(&self) -> io::Result<&str> {
788         str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL)
789     }
790 
791     #[inline]
to_string_lossynull792     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]
into_with_c_strnull814     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 
823 impl Arg for &Vec<u8> {
824     #[inline]
as_strnull825     fn as_str(&self) -> io::Result<&str> {
826         str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL)
827     }
828 
829     #[inline]
to_string_lossynull830     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]
into_with_c_strnull852     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 
861 impl Arg for Vec<u8> {
862     #[inline]
as_strnull863     fn as_str(&self) -> io::Result<&str> {
864         str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL)
865     }
866 
867     #[inline]
to_string_lossynull868     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]
into_with_c_strnull890     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")]
900 impl Arg for DecInt {
901     #[inline]
as_strnull902     fn as_str(&self) -> io::Result<&str> {
903         Ok(self.as_str())
904     }
905 
906     #[inline]
to_string_lossynull907     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]
into_with_c_strnull925     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]
with_c_strnull937 fn with_c_str<T, F>(bytes: &[u8], f: F) -> io::Result<T>
938 where
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]
with_c_str_slow_pathnull976 fn with_c_str_slow_path<T, F>(bytes: &[u8], f: F) -> io::Result<T>
977 where
978     F: FnOnce(&CStr) -> io::Result<T>,
979 {
980     f(&CString::new(bytes).map_err(|_cstr_err| io::Errno::INVAL)?)
981 }
982