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