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