├── .gitignore ├── .rustfmt.toml ├── .github ├── actions │ └── install-rust │ │ ├── action.yml │ │ ├── README.md │ │ └── main.js └── workflows │ └── main.yml ├── examples ├── portable-views.rs ├── easy-conversions.rs ├── owning-wrapper.rs ├── flexible-apis.rs └── hello.rs ├── COPYRIGHT ├── LICENSE-MIT ├── tests ├── assumptions.rs ├── ffi.rs ├── niche-optimizations.rs └── api.rs ├── Cargo.toml ├── src ├── example_ffi.rs ├── lib.rs ├── traits.rs ├── raw.rs ├── views.rs └── portability.rs ├── CODE_OF_CONDUCT.md ├── ORG_CODE_OF_CONDUCT.md ├── README.md ├── LICENSE-APACHE └── LICENSE-Apache-2.0_WITH_LLVM-exception /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # This file tells tools we use rustfmt. We use the default settings. 2 | -------------------------------------------------------------------------------- /.github/actions/install-rust/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Install Rust toolchain' 2 | description: 'Install both `rustup` and a Rust toolchain' 3 | 4 | inputs: 5 | toolchain: 6 | description: 'Default toolchan to install' 7 | required: false 8 | default: 'stable' 9 | 10 | runs: 11 | using: node20 12 | main: 'main.js' 13 | -------------------------------------------------------------------------------- /.github/actions/install-rust/README.md: -------------------------------------------------------------------------------- 1 | # install-rust 2 | 3 | A small github action to install `rustup` and a Rust toolchain. This is 4 | generally expressed inline, but it was repeated enough in this repository it 5 | seemed worthwhile to extract. 6 | 7 | Some gotchas: 8 | 9 | * Can't `--self-update` on Windows due to permission errors (a bug in Github 10 | Actions) 11 | * `rustup` isn't installed on macOS (a bug in Github Actions) 12 | 13 | When the above are fixed we should delete this action and just use this inline: 14 | 15 | ```yml 16 | - run: rustup update $toolchain && rustup default $toolchain 17 | shell: bash 18 | ``` 19 | -------------------------------------------------------------------------------- /examples/portable-views.rs: -------------------------------------------------------------------------------- 1 | //! io-lifetimes provides safe, convenient, and portable ways to temporarily 2 | //! view an I/O resource as a `File`, `Socket`, or other types. 3 | 4 | use io_lifetimes::AsFilelike; 5 | use std::fs::File; 6 | use std::io::{self, stdout}; 7 | 8 | fn main() -> io::Result<()> { 9 | let stdout = stdout(); 10 | 11 | // With `AsFilelike`, any type implementing `AsFd`/`AsHandle` can be viewed 12 | // as any type supporting `FromFilelike`, so you can call `File` methods on 13 | // `Stdout` or other things. 14 | // 15 | // Whether or not you can actually do this is up to the OS, of course. In 16 | // this case, Unix can do this, but it appears Windows can't. 17 | let metadata = stdout.as_filelike_view::().metadata()?; 18 | 19 | if metadata.is_file() { 20 | println!("stdout is a file!"); 21 | } else { 22 | println!("stdout is not a file!"); 23 | } 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Short version for non-lawyers: 2 | 3 | `io-lifetimes` is triple-licensed under Apache 2.0 with the LLVM Exception, 4 | Apache 2.0, and MIT terms. 5 | 6 | 7 | Longer version: 8 | 9 | Copyrights in the `io-lifetimes` project are retained by their contributors. 10 | No copyright assignment is required to contribute to the `io-lifetimes` 11 | project. 12 | 13 | Some files include code derived from Rust's `libstd`; see the comments in 14 | the code for details. 15 | 16 | Except as otherwise noted (below and/or in individual files), `io-lifetimes` 17 | is licensed under: 18 | 19 | - the Apache License, Version 2.0, with the LLVM Exception 20 | or 21 | 22 | - the Apache License, Version 2.0 23 | or 24 | , 25 | - or the MIT license 26 | or 27 | , 28 | 29 | at your option. 30 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /examples/easy-conversions.rs: -------------------------------------------------------------------------------- 1 | //! io-lifetimes provides safe, portable, and convenient conversions from types 2 | //! implementing `IntoFilelike` and `FromSocketlike` to types implementing 3 | //! `FromFilelike` and `IntoSocketlike`, respectively. 4 | 5 | use io_lifetimes::FromFilelike; 6 | use std::fs::File; 7 | use std::io::{self, Read}; 8 | use std::process::{Command, Stdio}; 9 | 10 | fn main() -> io::Result<()> { 11 | let mut child = Command::new("cargo") 12 | .arg("--help") 13 | .stdout(Stdio::piped()) 14 | .spawn() 15 | .expect("failed to execute child"); 16 | 17 | // Convert from `ChildStderr` into `File` without any platform-specific 18 | // code or `unsafe`! 19 | let mut file = File::from_into_filelike(child.stdout.take().unwrap()); 20 | 21 | // Well, this example is not actually that cool, because `File` doesn't let 22 | // you do anything that you couldn't already do with `ChildStderr` etc., but 23 | // it's useful outside of standard library types. 24 | let mut buffer = String::new(); 25 | file.read_to_string(&mut buffer)?; 26 | 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /tests/assumptions.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(target_os = "wasi", feature(wasi_ext))] 2 | 3 | #[cfg(any(not(windows), feature = "close"))] 4 | use std::mem::size_of; 5 | 6 | #[cfg(unix)] 7 | #[test] 8 | fn test_assumptions() { 9 | assert_eq!(size_of::(), size_of::()); 10 | assert_eq!( 11 | size_of::(), 12 | size_of::() 13 | ); 14 | } 15 | 16 | #[cfg(target_os = "wasi")] 17 | #[test] 18 | fn test_assumptions() { 19 | assert_eq!(size_of::(), size_of::()); 20 | assert_eq!( 21 | size_of::(), 22 | size_of::() 23 | ); 24 | } 25 | 26 | #[cfg(all(windows, feature = "close"))] 27 | #[test] 28 | fn test_assumptions() { 29 | assert_eq!( 30 | size_of::(), 31 | size_of::() 32 | ); 33 | assert_eq!( 34 | size_of::(), 35 | size_of::() 36 | ); 37 | assert_eq!( 38 | windows_sys::Win32::Networking::WinSock::INVALID_SOCKET, 39 | usize::MAX 40 | ); 41 | assert_ne!( 42 | windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE, 43 | std::ptr::null_mut() as std::os::windows::io::RawHandle as _ 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /.github/actions/install-rust/main.js: -------------------------------------------------------------------------------- 1 | const child_process = require('child_process'); 2 | const toolchain = process.env.INPUT_TOOLCHAIN; 3 | const fs = require('fs'); 4 | 5 | function set_env(name, val) { 6 | fs.appendFileSync(process.env['GITHUB_ENV'], `${name}=${val}\n`) 7 | } 8 | 9 | // Needed for now to get 1.24.2 which fixes a bug in 1.24.1 that causes issues 10 | // on Windows. 11 | if (process.platform === 'win32') { 12 | child_process.execFileSync('rustup', ['self', 'update']); 13 | } 14 | 15 | child_process.execFileSync('rustup', ['set', 'profile', 'minimal']); 16 | child_process.execFileSync('rustup', ['update', toolchain, '--no-self-update']); 17 | child_process.execFileSync('rustup', ['default', toolchain]); 18 | 19 | // Deny warnings on CI to keep our code warning-free as it lands in-tree. Don't 20 | // do this on nightly though since there's a fair amount of warning churn there. 21 | if (!toolchain.startsWith('nightly')) { 22 | set_env("RUSTFLAGS", "-D warnings"); 23 | } 24 | 25 | // Save disk space by avoiding incremental compilation, and also we don't use 26 | // any caching so incremental wouldn't help anyway. 27 | set_env("CARGO_INCREMENTAL", "0"); 28 | 29 | // Turn down debuginfo from 2 to 1 to help save disk space 30 | set_env("CARGO_PROFILE_DEV_DEBUG", "1"); 31 | set_env("CARGO_PROFILE_TEST_DEBUG", "1"); 32 | 33 | if (process.platform === 'darwin') { 34 | set_env("CARGO_PROFILE_DEV_SPLIT_DEBUGINFO", "unpacked"); 35 | set_env("CARGO_PROFILE_TEST_SPLIT_DEBUGINFO", "unpacked"); 36 | } 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "io-lifetimes" 3 | version = "3.0.1" 4 | description = "A low-level I/O ownership and borrowing library" 5 | authors = ["Dan Gohman "] 6 | license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" 7 | keywords = ["api", "io"] 8 | categories = ["os", "rust-patterns"] 9 | edition = "2021" 10 | repository = "https://github.com/sunfishcode/io-lifetimes" 11 | include = ["src", "build.rs", "Cargo.toml", "COPYRIGHT", "LICENSE*", "/*.md"] 12 | rust-version = "1.70" 13 | 14 | [dependencies] 15 | # io-lifetimes only depends on libc/windows-sys for the ability to close 16 | # and duplicate fds/handles/sockets. The following are just optional 17 | # dependencies to add foreign-type impls for the traits. 18 | 19 | [target.'cfg(not(target_os = "wasi"))'.dependencies] 20 | # Optionally depend on os_pipe to implement traits for its types for now. 21 | os_pipe = { version = "1.0.0", features = ["io_safety"], optional = true } 22 | 23 | # Optionally depend on async-std just to provide impls for its types. 24 | async-std = { version = "1.13.0", optional = true, features = ["io_safety"] } 25 | # Optionally depend on tokio to implement traits for its types. 26 | tokio = { version = "1.6.0", features = ["io-std", "fs", "net", "process"], optional = true } 27 | # Optionally depend on socket2 to implement traits for its types. 28 | socket2 = { version = "0.6.0", optional = true } 29 | # Optionally depend on mio to implement traits for its types. 30 | mio = { version = "1.0.0", features = ["net", "os-ext"], optional = true } 31 | 32 | [target.'cfg(target_os = "hermit")'.dependencies] 33 | hermit-abi = { version = ">=0.3, <=0.5", optional = true } 34 | 35 | [target.'cfg(not(windows))'.dependencies] 36 | libc = { version = "0.2.96", optional = true } 37 | 38 | [target.'cfg(windows)'.dependencies.windows-sys] 39 | version = ">=0.52, <=0.60" 40 | optional = true 41 | features = [ 42 | "Win32_Foundation", 43 | "Win32_Storage_FileSystem", 44 | "Win32_Networking_WinSock", 45 | "Win32_Security", 46 | "Win32_System_IO", 47 | ] 48 | 49 | [package.metadata.docs.rs] 50 | features = ["close"] 51 | 52 | [features] 53 | default = [] 54 | close = ["libc", "hermit-abi", "windows-sys"] 55 | 56 | [lints.rust.unexpected_cfgs] 57 | level = "warn" 58 | check-cfg = [ 59 | 'cfg(wasi_ext)', 60 | ] 61 | -------------------------------------------------------------------------------- /tests/ffi.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "close")] 2 | 3 | #[cfg(any(unix, windows))] 4 | use io_lifetimes::example_ffi::*; 5 | #[cfg(windows)] 6 | use io_lifetimes::{InvalidHandleError, OwnedHandle}; 7 | #[cfg(windows)] 8 | use std::{os::windows::io::RawHandle, ptr::null_mut}; 9 | #[cfg(windows)] 10 | use windows_sys::Win32::Storage::FileSystem::{ 11 | FILE_ATTRIBUTE_NORMAL, FILE_GENERIC_READ, OPEN_EXISTING, 12 | }; 13 | 14 | #[cfg(unix)] 15 | #[test] 16 | fn test_file_not_found() { 17 | assert!(unsafe { 18 | open( 19 | "/dev/no/such/file\0".as_ptr() as *const _, 20 | O_RDONLY | O_CLOEXEC, 21 | ) 22 | } 23 | .is_none()); 24 | } 25 | 26 | #[cfg(windows)] 27 | #[test] 28 | fn test_file_not_found() { 29 | let handle: Result = unsafe { 30 | CreateFileW( 31 | [ 32 | 'C' as u16, ':' as _, '/' as _, 'n' as _, 'o' as _, '/' as _, 's' as _, 'u' as _, 33 | 'c' as _, 'h' as _, '/' as _, 'f' as _, 'i' as _, 'l' as _, 'e' as _, 0, 34 | ] 35 | .as_ptr(), 36 | FILE_GENERIC_READ, 37 | 0, 38 | null_mut(), 39 | OPEN_EXISTING, 40 | FILE_ATTRIBUTE_NORMAL, 41 | null_mut() as RawHandle as HANDLE, 42 | ) 43 | } 44 | .try_into(); 45 | assert!(handle.is_err()); 46 | assert_eq!( 47 | std::io::Error::last_os_error().kind(), 48 | std::io::ErrorKind::NotFound 49 | ); 50 | } 51 | 52 | #[cfg(unix)] 53 | #[test] 54 | fn test_file_found() { 55 | assert!(unsafe { open("Cargo.toml\0".as_ptr() as *const _, O_RDONLY | O_CLOEXEC) }.is_some()); 56 | } 57 | 58 | #[cfg(windows)] 59 | #[test] 60 | fn test_file_found() { 61 | let handle: Result = unsafe { 62 | CreateFileW( 63 | [ 64 | 'C' as u16, 'a' as _, 'r' as _, 'g' as _, 'o' as _, '.' as _, 't' as _, 'o' as _, 65 | 'm' as _, 'l' as _, 0, 66 | ] 67 | .as_ptr(), 68 | FILE_GENERIC_READ, 69 | 0, 70 | null_mut(), 71 | OPEN_EXISTING, 72 | FILE_ATTRIBUTE_NORMAL, 73 | null_mut() as RawHandle as HANDLE, 74 | ) 75 | } 76 | .try_into(); 77 | assert!(handle.is_ok()); 78 | } 79 | -------------------------------------------------------------------------------- /tests/niche-optimizations.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(target_os = "wasi", feature(wasi_ext))] 2 | 3 | use std::mem::size_of; 4 | 5 | #[cfg(any(unix, target_os = "wasi"))] 6 | use io_lifetimes::{BorrowedFd, OwnedFd}; 7 | #[cfg(windows)] 8 | use io_lifetimes::{BorrowedSocket, OwnedSocket}; 9 | 10 | #[cfg(unix)] 11 | use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd}; 12 | #[cfg(target_os = "wasi")] 13 | use std::os::wasi::io::{FromRawFd, IntoRawFd, RawFd}; 14 | #[cfg(windows)] 15 | use std::os::windows::io::{FromRawSocket, IntoRawSocket, RawSocket}; 16 | 17 | #[cfg(any(unix, target_os = "wasi"))] 18 | #[test] 19 | fn test_niche_optimizations() { 20 | assert_eq!(size_of::>(), size_of::()); 21 | assert_eq!(size_of::>>(), size_of::()); 22 | unsafe { 23 | assert_eq!(OwnedFd::from_raw_fd(RawFd::MIN).into_raw_fd(), RawFd::MIN); 24 | assert_eq!(OwnedFd::from_raw_fd(RawFd::MAX).into_raw_fd(), RawFd::MAX); 25 | assert_eq!( 26 | Some(OwnedFd::from_raw_fd(RawFd::MIN)) 27 | .unwrap() 28 | .into_raw_fd(), 29 | RawFd::MIN 30 | ); 31 | assert_eq!( 32 | Some(OwnedFd::from_raw_fd(RawFd::MAX)) 33 | .unwrap() 34 | .into_raw_fd(), 35 | RawFd::MAX 36 | ); 37 | } 38 | } 39 | 40 | #[cfg(windows)] 41 | #[test] 42 | fn test_niche_optimizations_socket() { 43 | assert_eq!(size_of::>(), size_of::()); 44 | assert_eq!( 45 | size_of::>>(), 46 | size_of::(), 47 | ); 48 | unsafe { 49 | #[cfg(target_pointer_width = "32")] 50 | let (min, max) = (i32::MIN as u32, i32::MAX as u32); 51 | #[cfg(target_pointer_width = "64")] 52 | let (min, max) = (i64::MIN as u64, i64::MAX as u64); 53 | 54 | assert_eq!(OwnedSocket::from_raw_socket(min).into_raw_socket(), min); 55 | assert_eq!(OwnedSocket::from_raw_socket(max).into_raw_socket(), max); 56 | assert_eq!( 57 | Some(OwnedSocket::from_raw_socket(min)) 58 | .unwrap() 59 | .into_raw_socket(), 60 | min 61 | ); 62 | assert_eq!( 63 | Some(OwnedSocket::from_raw_socket(max)) 64 | .unwrap() 65 | .into_raw_socket(), 66 | max 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/example_ffi.rs: -------------------------------------------------------------------------------- 1 | //! This is just a sample of what FFI using this crate can look like. 2 | 3 | #![allow(missing_docs)] 4 | 5 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 6 | use crate::{BorrowedFd, OwnedFd}; 7 | #[cfg(windows)] 8 | use crate::{BorrowedHandle, HandleOrInvalid}; 9 | 10 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 11 | use libc::{c_char, c_int, c_void, size_t, ssize_t}; 12 | #[cfg(windows)] 13 | use { 14 | core::ffi::c_void, 15 | windows_sys::core::PCWSTR, 16 | windows_sys::Win32::Foundation::BOOL, 17 | windows_sys::Win32::Security::SECURITY_ATTRIBUTES, 18 | windows_sys::Win32::Storage::FileSystem::{ 19 | FILE_CREATION_DISPOSITION, FILE_FLAGS_AND_ATTRIBUTES, FILE_SHARE_MODE, 20 | }, 21 | windows_sys::Win32::System::IO::OVERLAPPED, 22 | }; 23 | 24 | // Declare a few FFI functions ourselves, to show off the FFI ergonomics. 25 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 26 | extern "C" { 27 | pub fn open(pathname: *const c_char, flags: c_int, ...) -> Option; 28 | } 29 | #[cfg(any(unix, target_os = "wasi"))] 30 | extern "C" { 31 | pub fn read(fd: BorrowedFd<'_>, ptr: *mut c_void, size: size_t) -> ssize_t; 32 | pub fn write(fd: BorrowedFd<'_>, ptr: *const c_void, size: size_t) -> ssize_t; 33 | } 34 | #[cfg(any(unix, target_os = "wasi"))] 35 | pub use libc::{O_CLOEXEC, O_CREAT, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; 36 | 37 | // The Windows analogs of the above. Note the use of [`HandleOrInvalid`] as 38 | // the return type for `CreateFileW`, since that function is defined to return 39 | // [`INVALID_HANDLE_VALUE`] on error instead of null. 40 | #[cfg(windows)] 41 | extern "system" { 42 | pub fn CreateFileW( 43 | lpfilename: PCWSTR, 44 | dwdesiredaccess: u32, 45 | dwsharemode: FILE_SHARE_MODE, 46 | lpsecurityattributes: *const SECURITY_ATTRIBUTES, 47 | dwcreationdisposition: FILE_CREATION_DISPOSITION, 48 | dwflagsandattributes: FILE_FLAGS_AND_ATTRIBUTES, 49 | htemplatefile: HANDLE, 50 | ) -> HandleOrInvalid; 51 | pub fn ReadFile( 52 | hfile: BorrowedHandle<'_>, 53 | lpbuffer: *mut c_void, 54 | nnumberofbytestoread: u32, 55 | lpnumberofbytesread: *mut u32, 56 | lpoverlapped: *mut OVERLAPPED, 57 | ) -> BOOL; 58 | pub fn WriteFile( 59 | hfile: BorrowedHandle<'_>, 60 | lpbuffer: *const c_void, 61 | nnumberofbytestowrite: u32, 62 | lpnumberofbyteswritten: *mut u32, 63 | lpoverlapped: *mut OVERLAPPED, 64 | ) -> BOOL; 65 | } 66 | 67 | #[cfg(windows)] 68 | pub use { 69 | windows_sys::Win32::Foundation::HANDLE, 70 | windows_sys::Win32::Storage::FileSystem::{ 71 | CREATE_ALWAYS, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, FILE_GENERIC_READ, FILE_GENERIC_WRITE, 72 | OPEN_EXISTING, 73 | }, 74 | }; 75 | -------------------------------------------------------------------------------- /examples/owning-wrapper.rs: -------------------------------------------------------------------------------- 1 | //! A simple example implementing the main traits for a type. 2 | 3 | use io_lifetimes::OwnedFilelike; 4 | #[cfg(not(windows))] 5 | use io_lifetimes::{AsFd, BorrowedFd, FromFd, OwnedFd}; 6 | #[cfg(windows)] 7 | use io_lifetimes::{AsHandle, BorrowedHandle, FromHandle, OwnedHandle}; 8 | 9 | /// A wrapper around a file descriptor. 10 | /// 11 | /// Implementing `AsFd`, `Into`, and `From` for a type that 12 | /// wraps an `Owned*` is straightforward. `Owned*` types also automatically 13 | /// close the handle in its `Drop`. 14 | /// 15 | /// Should owning wrappers implement `AsRawFd`, `IntoRawFd`, and `FromRawFd` 16 | /// too? They can, and there's no need to remove them from a type that already 17 | /// implements them. But for new code, they can be omitted. Users that really 18 | /// need the raw value can always do `as_fd().as_raw_fd()`, 19 | /// `.into_fd().into_raw_fd()`, or `T::from_fd(OwnedFd::from_raw_fd(raw_fd))`. 20 | /// But if possible, users should use just `as_fd`, `into_fd`, and `from_fd` 21 | /// and avoid working with raw values altogether. 22 | struct Thing { 23 | filelike: OwnedFilelike, 24 | } 25 | 26 | #[cfg(not(windows))] 27 | impl AsFd for Thing { 28 | #[inline] 29 | fn as_fd(&self) -> BorrowedFd<'_> { 30 | self.filelike.as_fd() 31 | } 32 | } 33 | 34 | #[cfg(not(windows))] 35 | impl From for OwnedFd { 36 | #[inline] 37 | fn from(owned: Thing) -> Self { 38 | owned.filelike 39 | } 40 | } 41 | 42 | #[cfg(not(windows))] 43 | impl From for Thing { 44 | #[inline] 45 | fn from(filelike: OwnedFd) -> Self { 46 | Self { filelike } 47 | } 48 | } 49 | 50 | #[cfg(windows)] 51 | impl AsHandle for Thing { 52 | #[inline] 53 | fn as_handle(&self) -> BorrowedHandle<'_> { 54 | self.filelike.as_handle() 55 | } 56 | } 57 | 58 | #[cfg(windows)] 59 | impl From for OwnedHandle { 60 | #[inline] 61 | fn from(owned: Thing) -> Self { 62 | owned.filelike 63 | } 64 | } 65 | 66 | #[cfg(windows)] 67 | impl From for Thing { 68 | #[inline] 69 | fn from(filelike: OwnedHandle) -> Self { 70 | Self { filelike } 71 | } 72 | } 73 | 74 | fn main() { 75 | use io_lifetimes::{AsFilelike, FromFilelike, IntoFilelike}; 76 | 77 | // Minimally exercise `Thing`'s Posix-ish API. 78 | #[cfg(not(windows))] 79 | { 80 | let file = std::fs::File::open("Cargo.toml").unwrap(); 81 | let thing = Thing::from_into_fd(file); 82 | let _ = thing.as_fd(); 83 | let _: OwnedFd = thing.into(); 84 | } 85 | 86 | // Minimally exercise `Thing`'s Windows API. 87 | #[cfg(windows)] 88 | { 89 | let file = std::fs::File::open("Cargo.toml").unwrap(); 90 | let thing = Thing::from_into_handle(file); 91 | let _ = thing.as_handle(); 92 | let _: OwnedHandle = thing.into(); 93 | } 94 | 95 | // Implementing the above traits makes the blanket impls for the portable 96 | // `Filelike` traits available too. 97 | { 98 | let file = std::fs::File::open("Cargo.toml").unwrap(); 99 | let thing = Thing::from_into_filelike(file); 100 | let _ = thing.as_filelike(); 101 | let _ = thing.into_filelike(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | *Note*: this Code of Conduct pertains to individuals' behavior. Please also see the [Organizational Code of Conduct][OCoC]. 4 | 5 | ## Our Pledge 6 | 7 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to creating a positive environment include: 12 | 13 | * Using welcoming and inclusive language 14 | * Being respectful of differing viewpoints and experiences 15 | * Gracefully accepting constructive criticism 16 | * Focusing on what is best for the community 17 | * Showing empathy towards other community members 18 | 19 | Examples of unacceptable behavior by participants include: 20 | 21 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 22 | * Trolling, insulting/derogatory comments, and personal or political attacks 23 | * Public or private harassment 24 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 25 | * Other conduct which could reasonably be considered inappropriate in a professional setting 26 | 27 | ## Our Responsibilities 28 | 29 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 30 | 31 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 32 | 33 | ## Scope 34 | 35 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 36 | 37 | ## Enforcement 38 | 39 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Bytecode Alliance CoC team at [report@bytecodealliance.org](mailto:report@bytecodealliance.org). The CoC team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The CoC team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 40 | 41 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the Bytecode Alliance's leadership. 42 | 43 | ## Attribution 44 | 45 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 46 | 47 | [OCoC]: https://github.com/sunfishcode/io-lifetimes/blob/main/ORG_CODE_OF_CONDUCT.md 48 | [homepage]: https://www.contributor-covenant.org 49 | [version]: https://www.contributor-covenant.org/version/1/4/ 50 | -------------------------------------------------------------------------------- /examples/flexible-apis.rs: -------------------------------------------------------------------------------- 1 | //! io-lifetimes provides two different options for library authors 2 | //! writing APIs which accept untyped I/O resources. 3 | //! 4 | //! The following uses the POSIX-ish `Fd` types; similar considerations 5 | //! apply to the Windows and portable types. 6 | 7 | #[cfg(not(windows))] 8 | use io_lifetimes::{AsFd, BorrowedFd, OwnedFd}; 9 | 10 | /// The simplest way to accept a borrowed I/O resource is to simply use a 11 | /// `BorrwedFd` as an argument. This doesn't require the function to have any 12 | /// type parameters. It also works in FFI signatures, as `BorrowedFd` and (on 13 | /// Rust >= 1.63) `Option` are guaranteed to have the same layout 14 | /// as `RawFd`. 15 | /// 16 | /// Callers with an `AsFd`-implementing type would call `.as_fd()` and pass 17 | /// the result. 18 | #[cfg(not(windows))] 19 | fn use_fd_a(fd: BorrowedFd<'_>) { 20 | let _ = fd; 21 | } 22 | 23 | /// Another way to do this is to use an `AsFd` type parameter. This is more 24 | /// verbose at the function definition site, and entails monomorphization, but 25 | /// it has the advantage of allowing users to pass in any type implementing 26 | /// `AsFd` directly, without having to call `.as_fd()` themselves. 27 | #[cfg(not(windows))] 28 | fn use_fd_b(fd: Fd) { 29 | let _ = fd.as_fd(); 30 | } 31 | 32 | /// Another way to do this is to use an `impl AsFd` parameter. 33 | #[cfg(not(windows))] 34 | fn use_fd_c(fd: impl AsFd) { 35 | let _ = fd.as_fd(); 36 | } 37 | 38 | /// The simplest way to accept a consumed I/O resource is to simply use an 39 | /// `OwnedFd` as an argument. Similar to `use_fd_a`, this doesn't require the 40 | /// function to have any type parameters, and also works in FFI signatures. 41 | /// 42 | /// Callers with an `IntoFd`-implementing type would call `.into_fd()` and pass 43 | /// the result. 44 | #[cfg(not(windows))] 45 | fn consume_fd_a(fd: OwnedFd) { 46 | let _ = fd; 47 | } 48 | 49 | /// Another way to do this is to use an `IntoFd` type parameter. Similar to 50 | /// `use_fd_b`, this is more verbose here and entails monomorphization, but it 51 | /// has the advantage of allowing users to pass in any type implementing 52 | /// `IntoFd` directly. 53 | #[cfg(not(windows))] 54 | fn consume_fd_b>(fd: Fd) { 55 | let _: OwnedFd = fd.into(); 56 | } 57 | 58 | /// Another way to do this is to use an `impl IntoFd` parameter. 59 | #[cfg(not(windows))] 60 | fn consume_fd_c(fd: impl Into) { 61 | let _: OwnedFd = fd.into(); 62 | } 63 | 64 | /// Now let's see how the APIs look for users. 65 | #[cfg(not(windows))] 66 | fn main() { 67 | let f = std::fs::File::open("Cargo.toml").unwrap(); 68 | 69 | // The simple option requires an `.as_fd()` at the callsite. 70 | use_fd_a(f.as_fd()); 71 | 72 | // Another option can take a reference to any owning type directly. 73 | use_fd_b(&f); 74 | 75 | // Of course, users can still pass in `BorrowedFd` values if they want to. 76 | use_fd_b(f.as_fd()); 77 | 78 | // The other option is `impl AsFd`. 79 | use_fd_c(&f); 80 | 81 | // Users can still pass in `BorrowedFd` values if they want to here too. 82 | use_fd_c(f.as_fd()); 83 | 84 | let a = std::fs::File::open("Cargo.toml").unwrap(); 85 | let b = std::fs::File::open("Cargo.toml").unwrap(); 86 | let c = std::fs::File::open("Cargo.toml").unwrap(); 87 | 88 | // The simple option requires an `.into()` at the callsite. 89 | consume_fd_a(a.into()); 90 | 91 | // Another option can take any `Into` type directly. 92 | consume_fd_b(b); 93 | 94 | // The other option can take any `Into` type directly. 95 | consume_fd_c(c); 96 | } 97 | 98 | #[cfg(windows)] 99 | fn main() { 100 | println!("This example uses non-Windows APIs."); 101 | } 102 | -------------------------------------------------------------------------------- /examples/hello.rs: -------------------------------------------------------------------------------- 1 | //! A simple testcase that prints a few messages to the console, demonstrating 2 | //! the io-lifetimes API. 3 | 4 | #[cfg(feature = "close")] 5 | use io_lifetimes::example_ffi::*; 6 | #[cfg(feature = "close")] 7 | use std::{ 8 | fs::File, 9 | io::{self, Write}, 10 | }; 11 | 12 | #[cfg(all(unix, feature = "close"))] 13 | use io_lifetimes::{AsFd, OwnedFd}; 14 | 15 | #[cfg(all(windows, feature = "close"))] 16 | use io_lifetimes::{AsHandle, OwnedHandle}; 17 | #[cfg(all(windows, feature = "close"))] 18 | use std::{os::windows::io::RawHandle, ptr::null_mut}; 19 | 20 | #[cfg(all(unix, feature = "close"))] 21 | fn main() -> io::Result<()> { 22 | let fd = unsafe { 23 | // Open a file, which returns an `Option`, which we can 24 | // maybe convert into an `OwnedFile`. 25 | let fd: OwnedFd = open("/dev/stdout\0".as_ptr() as *const _, O_WRONLY | O_CLOEXEC) 26 | .ok_or_else(io::Error::last_os_error)?; 27 | 28 | // Borrow the fd to write to it. 29 | let result = write(fd.as_fd(), "hello, world\n".as_ptr() as *const _, 13); 30 | match result { 31 | -1 => return Err(io::Error::last_os_error()), 32 | 13 => (), 33 | _ => return Err(io::Error::new(io::ErrorKind::Other, "short write")), 34 | } 35 | 36 | fd 37 | }; 38 | 39 | // Convert into a `File`. No `unsafe` here! 40 | let mut file = File::from(fd); 41 | writeln!(&mut file, "greetings, y'all")?; 42 | 43 | // We can borrow a `BorrowedFd` from a `File`. 44 | unsafe { 45 | let result = write(file.as_fd(), "sup?\n".as_ptr() as *const _, 5); 46 | match result { 47 | -1 => return Err(io::Error::last_os_error()), 48 | 5 => (), 49 | _ => return Err(io::Error::new(io::ErrorKind::Other, "short write")), 50 | } 51 | } 52 | 53 | // `OwnedFd` closes the fd in its `Drop` implementation. 54 | 55 | Ok(()) 56 | } 57 | 58 | /// The Windows analog of the above. 59 | #[cfg(all(windows, feature = "close"))] 60 | fn main() -> io::Result<()> { 61 | let handle = unsafe { 62 | // Open a file, which returns an `HandleOrInvalid`, which we can fallibly 63 | // convert into an `OwnedFile`. 64 | let handle: OwnedHandle = CreateFileW( 65 | ['C' as u16, 'O' as _, 'N' as _, 0].as_ptr(), 66 | FILE_GENERIC_WRITE, 67 | 0, 68 | null_mut(), 69 | OPEN_EXISTING, 70 | FILE_ATTRIBUTE_NORMAL, 71 | null_mut() as RawHandle as HANDLE, 72 | ) 73 | .try_into() 74 | .map_err(|_err| io::Error::last_os_error())?; 75 | 76 | // Borrow the handle to write to it. 77 | let mut number_of_bytes_written = 0; 78 | let result = WriteFile( 79 | handle.as_handle(), 80 | "hello, world\n".as_ptr() as *const _, 81 | 13, 82 | &mut number_of_bytes_written, 83 | null_mut(), 84 | ); 85 | match (result, number_of_bytes_written) { 86 | (0, _) => return Err(io::Error::last_os_error()), 87 | (_, 13) => (), 88 | (_, _) => return Err(io::Error::new(io::ErrorKind::Other, "short write")), 89 | } 90 | 91 | handle 92 | }; 93 | 94 | // Convert into a `File`. No `unsafe` here! 95 | let mut file = File::from(handle); 96 | writeln!(&mut file, "greetings, y'all")?; 97 | 98 | // We can borrow a `BorrowedHandle` from a `File`. 99 | unsafe { 100 | let mut number_of_bytes_written = 0; 101 | let result = WriteFile( 102 | file.as_handle(), 103 | "sup?\n".as_ptr() as *const _, 104 | 5, 105 | &mut number_of_bytes_written, 106 | null_mut(), 107 | ); 108 | match (result, number_of_bytes_written) { 109 | (0, _) => return Err(io::Error::last_os_error()), 110 | (_, 5) => (), 111 | (_, _) => return Err(io::Error::new(io::ErrorKind::Other, "short write")), 112 | } 113 | } 114 | 115 | // `OwnedHandle` closes the handle in its `Drop` implementation. 116 | 117 | Ok(()) 118 | } 119 | 120 | #[cfg(all( 121 | not(all(unix, feature = "close")), 122 | not(all(windows, feature = "close")) 123 | ))] 124 | fn main() { 125 | println!("This example requires the \"close\" feature."); 126 | } 127 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Experimental new types and traits to replace the `Raw` family of types and 2 | //! traits. 3 | //! 4 | //! This API has much conceptual similarity with the `Raw` API, but introduces 5 | //! explicit concepts of ownership and borrowing: 6 | //! 7 | //! | `Raw` API | This experimental API | 8 | //! | ---------- | ------------------------ | 9 | //! | `Raw*` | `Borrowed*` and `Owned*` | 10 | //! | `AsRaw*` | `As*` | 11 | //! | `IntoRaw*` | `Into*` | 12 | //! | `FromRaw*` | `From*` | 13 | //! 14 | //! This gives it several advantages: 15 | //! 16 | //! - Less `unsafe` in user code! 17 | //! 18 | //! - Easier to understand ownership. 19 | //! 20 | //! - It avoids the inconsistency where `AsRawFd` and `IntoRawFd` return 21 | //! `RawFd` values that users ought to be able to trust, but aren't unsafe, 22 | //! so it's possible to fail to uphold this trust in purely safe Rust. 23 | //! 24 | //! - It enables a number of safe and portable convenience features, such as 25 | //! [safe typed views] and [from+into conversions]. 26 | //! 27 | //! [safe typed views]: AsFilelike::as_filelike_view 28 | //! [from+into conversions]: FromFilelike::from_into_filelike 29 | 30 | #![deny(missing_docs)] 31 | // Work around . 32 | #![cfg_attr(all(wasi_ext, target_os = "wasi"), feature(wasi_ext))] 33 | // Currently supported platforms. 34 | #![cfg(any(unix, windows, target_os = "wasi", target_os = "hermit"))] 35 | #![cfg_attr(docsrs, feature(doc_cfg))] 36 | 37 | mod portability; 38 | mod traits; 39 | 40 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 41 | #[allow(deprecated)] 42 | pub use traits::{FromFd, IntoFd}; 43 | #[cfg(windows)] 44 | #[allow(deprecated)] 45 | pub use traits::{FromHandle, FromSocket, IntoHandle, IntoSocket}; 46 | 47 | #[cfg(target_os = "hermit")] 48 | pub use std::os::hermit::io::{AsFd, BorrowedFd, OwnedFd}; 49 | #[cfg(unix)] 50 | pub use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; 51 | #[cfg(target_os = "wasi")] 52 | pub use std::os::wasi::io::{AsFd, BorrowedFd, OwnedFd}; 53 | #[cfg(windows)] 54 | pub use std::os::windows::io::{ 55 | AsHandle, AsSocket, BorrowedHandle, BorrowedSocket, HandleOrInvalid, InvalidHandleError, 56 | NullHandleError, OwnedHandle, OwnedSocket, 57 | }; 58 | 59 | // io-lifetimes defined `FromFd`/`IntoFd` traits instead of just using 60 | // `From`/`Into` because that allowed it to implement them for foreign types, 61 | // including std types like File and TcpStream, and popular third-party types. 62 | // 63 | // std just uses `From`/`Into`, because it defines those traits itself so it 64 | // can implement them for std types itself, and std won't be implementing them 65 | // for third-party types. However, this means that until `OwnedFd` et al are 66 | // stabilized, there will be no impls for third-party traits. 67 | // 68 | // So we define `FromFd`/`IntoFd` traits, and implement them in terms of 69 | // `From`/`Into`, 70 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 71 | #[allow(deprecated)] 72 | impl> FromFd for T { 73 | #[inline] 74 | fn from_fd(owned_fd: OwnedFd) -> Self { 75 | owned_fd.into() 76 | } 77 | } 78 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 79 | #[allow(deprecated)] 80 | impl IntoFd for T 81 | where 82 | OwnedFd: From, 83 | { 84 | #[inline] 85 | fn into_fd(self) -> OwnedFd { 86 | self.into() 87 | } 88 | } 89 | 90 | #[cfg(windows)] 91 | #[allow(deprecated)] 92 | impl> FromHandle for T { 93 | #[inline] 94 | fn from_handle(owned_handle: OwnedHandle) -> Self { 95 | owned_handle.into() 96 | } 97 | } 98 | #[cfg(windows)] 99 | #[allow(deprecated)] 100 | impl IntoHandle for T 101 | where 102 | OwnedHandle: From, 103 | { 104 | #[inline] 105 | fn into_handle(self) -> OwnedHandle { 106 | self.into() 107 | } 108 | } 109 | 110 | #[cfg(windows)] 111 | #[allow(deprecated)] 112 | impl> FromSocket for T { 113 | #[inline] 114 | fn from_socket(owned_socket: OwnedSocket) -> Self { 115 | owned_socket.into() 116 | } 117 | } 118 | #[cfg(windows)] 119 | #[allow(deprecated)] 120 | impl IntoSocket for T 121 | where 122 | OwnedSocket: From, 123 | { 124 | #[inline] 125 | fn into_socket(self) -> OwnedSocket { 126 | self.into() 127 | } 128 | } 129 | 130 | pub use portability::{ 131 | AsFilelike, AsSocketlike, BorrowedFilelike, BorrowedSocketlike, FromFilelike, FromSocketlike, 132 | IntoFilelike, IntoSocketlike, OwnedFilelike, OwnedSocketlike, 133 | }; 134 | 135 | #[cfg(feature = "close")] 136 | #[cfg_attr(docsrs, doc(cfg(feature = "close")))] 137 | pub mod example_ffi; 138 | pub mod raw; 139 | pub mod views; 140 | -------------------------------------------------------------------------------- /tests/api.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(target_os = "wasi", feature(wasi_ext))] 2 | #![cfg(feature = "close")] 3 | 4 | use io_lifetimes::raw::{AsRawFilelike, AsRawSocketlike}; 5 | use io_lifetimes::views::{FilelikeView, SocketlikeView}; 6 | use io_lifetimes::{ 7 | AsFilelike, AsSocketlike, BorrowedFilelike, FromFilelike, FromSocketlike, IntoFilelike, 8 | IntoSocketlike, 9 | }; 10 | use std::io::{Read, Write}; 11 | 12 | struct Tester {} 13 | impl Tester { 14 | fn use_file(filelike: Filelike) { 15 | let mut buf = Vec::new(); 16 | 17 | let filelike = filelike.as_filelike(); 18 | 19 | let view = filelike.as_filelike_view::(); 20 | let _ = (&*view).read(&mut buf).is_ok(); 21 | let _ = (&*view).write(&buf).is_ok(); 22 | 23 | let view = unsafe { 24 | FilelikeView::::view_raw( 25 | filelike 26 | .as_filelike_view::() 27 | .as_raw_filelike(), 28 | ) 29 | }; 30 | let _ = (&*view).read(&mut buf).is_ok(); 31 | let _ = (&*view).write(&buf).is_ok(); 32 | 33 | let _ = dbg!(filelike); 34 | } 35 | 36 | fn use_socket(socketlike: Socketlike) { 37 | let mut buf = Vec::new(); 38 | 39 | let socketlike = socketlike.as_socketlike(); 40 | let view = socketlike.as_socketlike_view::(); 41 | let _ = (&*view).read(&mut buf).is_ok(); 42 | let _ = (&*view).write(&buf).is_ok(); 43 | 44 | let view = unsafe { 45 | SocketlikeView::::view_raw( 46 | socketlike 47 | .as_socketlike_view::() 48 | .as_raw_socketlike(), 49 | ) 50 | }; 51 | let _ = (&*view).read(&mut buf).is_ok(); 52 | let _ = (&*view).write(&buf).is_ok(); 53 | 54 | let _ = dbg!(socketlike); 55 | } 56 | 57 | fn from_file(filelike: Filelike) { 58 | let mut buf = Vec::new(); 59 | 60 | let filelike = filelike.into_filelike(); 61 | let view = filelike.as_filelike_view::(); 62 | let _ = (&*view).read(&mut buf).is_ok(); 63 | let _ = (&*view).write(&buf).is_ok(); 64 | drop(view); 65 | 66 | let _ = dbg!(&filelike); 67 | let _ = std::fs::File::from_filelike(filelike); 68 | } 69 | 70 | fn from_socket(socketlike: Socketlike) { 71 | let mut buf = Vec::new(); 72 | 73 | let socketlike = socketlike.into_socketlike(); 74 | let view = socketlike.as_socketlike_view::(); 75 | let _ = (&*view).read(&mut buf).is_ok(); 76 | let _ = (&*view).write(&buf).is_ok(); 77 | drop(view); 78 | 79 | let _ = dbg!(&socketlike); 80 | let _ = std::net::TcpStream::from_socketlike(socketlike); 81 | } 82 | 83 | fn from_into_file(filelike: Filelike) { 84 | let _ = std::fs::File::from_into_filelike(filelike); 85 | } 86 | 87 | fn from_into_socket(socketlike: Socketlike) { 88 | let _ = std::net::TcpStream::from_into_socketlike(socketlike); 89 | } 90 | } 91 | 92 | #[test] 93 | fn test_api() { 94 | let file = std::fs::File::open("Cargo.toml").unwrap(); 95 | Tester::use_file(&file); 96 | Tester::use_file(file.as_filelike()); 97 | Tester::use_file(&*file.as_filelike_view::()); 98 | Tester::use_file(file.as_filelike_view::().as_filelike()); 99 | 100 | let socket = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); 101 | Tester::use_socket(&socket); 102 | Tester::use_socket(socket.as_socketlike()); 103 | Tester::use_socket(&*socket.as_socketlike_view::()); 104 | Tester::use_socket( 105 | socket 106 | .as_socketlike_view::() 107 | .as_socketlike(), 108 | ); 109 | 110 | Tester::from_file(std::fs::File::open("Cargo.toml").unwrap().into_filelike()); 111 | Tester::from_file( 112 | std::fs::File::open("Cargo.toml") 113 | .unwrap() 114 | .into_filelike() 115 | .into_filelike(), 116 | ); 117 | Tester::from_socket( 118 | std::net::TcpListener::bind("127.0.0.1:0") 119 | .unwrap() 120 | .into_socketlike(), 121 | ); 122 | Tester::from_socket( 123 | std::net::TcpListener::bind("127.0.0.1:0") 124 | .unwrap() 125 | .into_socketlike() 126 | .into_socketlike(), 127 | ); 128 | 129 | Tester::from_into_file(std::fs::File::open("Cargo.toml").unwrap().into_filelike()); 130 | Tester::from_into_socket( 131 | std::net::TcpListener::bind("127.0.0.1:0") 132 | .unwrap() 133 | .into_socketlike(), 134 | ); 135 | } 136 | 137 | #[test] 138 | fn test_as() { 139 | let file = std::fs::File::open("Cargo.toml").unwrap(); 140 | let borrow: BorrowedFilelike = file.as_filelike(); 141 | let reborrow: BorrowedFilelike = borrow.as_filelike(); 142 | let ref_reborrow: &BorrowedFilelike = &reborrow; 143 | let borrow_ref_reborrow: BorrowedFilelike = ref_reborrow.as_filelike(); 144 | let _ref_borrow_ref_reborrow: &BorrowedFilelike = &borrow_ref_reborrow; 145 | } 146 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | rustfmt: 11 | name: Rustfmt 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | submodules: true 17 | - uses: ./.github/actions/install-rust 18 | with: 19 | toolchain: stable 20 | - run: cargo fmt --all -- --check 21 | 22 | check: 23 | name: Check 24 | runs-on: ${{ matrix.os }} 25 | strategy: 26 | matrix: 27 | build: [stable, nightly] 28 | include: 29 | - build: stable 30 | os: ubuntu-latest 31 | rust: stable 32 | - build: nightly 33 | os: ubuntu-latest 34 | rust: nightly 35 | 36 | env: 37 | # -D warnings is commented out in our install-rust action; re-add it here. 38 | RUSTFLAGS: -D warnings 39 | steps: 40 | - uses: actions/checkout@v4 41 | with: 42 | submodules: true 43 | - uses: ./.github/actions/install-rust 44 | with: 45 | toolchain: ${{ matrix.rust }} 46 | 47 | - run: rustup target add x86_64-apple-darwin 48 | - run: cargo check --workspace --release -vv 49 | - run: cargo check --workspace --release -vv --no-default-features 50 | - run: cargo check --workspace --release -vv --target=x86_64-apple-darwin 51 | - run: cargo check --workspace --release -vv --target=x86_64-apple-darwin --no-default-features 52 | 53 | check-1_70: 54 | name: Check 55 | runs-on: ${{ matrix.os }} 56 | strategy: 57 | matrix: 58 | build: [1.70.0] 59 | include: 60 | - build: 1.70.0 61 | os: ubuntu-latest 62 | rust: 1.70.0 63 | 64 | env: 65 | # -D warnings is commented out in our install-rust action; re-add it here. 66 | RUSTFLAGS: -D warnings 67 | steps: 68 | - uses: actions/checkout@v4 69 | with: 70 | submodules: true 71 | - uses: ./.github/actions/install-rust 72 | with: 73 | toolchain: ${{ matrix.rust }} 74 | 75 | - run: rustup target add x86_64-apple-darwin 76 | - run: cargo check --workspace --release -vv 77 | - run: cargo check --workspace --release -vv --target=x86_64-apple-darwin 78 | 79 | check-windows: 80 | name: Check Windows 81 | runs-on: ${{ matrix.os }} 82 | strategy: 83 | matrix: 84 | build: [stable, nightly] 85 | include: 86 | - build: stable 87 | os: windows-latest 88 | rust: stable 89 | - build: nightly 90 | os: windows-latest 91 | rust: nightly 92 | 93 | env: 94 | # -D warnings is commented out in our install-rust action; re-add it here. 95 | RUSTFLAGS: -D warnings 96 | steps: 97 | - uses: actions/checkout@v4 98 | with: 99 | submodules: true 100 | - uses: ./.github/actions/install-rust 101 | with: 102 | toolchain: ${{ matrix.rust }} 103 | 104 | - run: cargo check --workspace --release -vv 105 | - run: cargo check --workspace --release -vv --no-default-features 106 | 107 | check-windows-1_70: 108 | name: Check Windows 109 | runs-on: ${{ matrix.os }} 110 | strategy: 111 | matrix: 112 | build: [1.70.0] 113 | include: 114 | - build: 1.70.0 115 | os: windows-latest 116 | rust: 1.70.0 117 | 118 | env: 119 | # -D warnings is commented out in our install-rust action; re-add it here. 120 | RUSTFLAGS: -D warnings 121 | steps: 122 | - uses: actions/checkout@v4 123 | with: 124 | submodules: true 125 | - uses: ./.github/actions/install-rust 126 | with: 127 | toolchain: ${{ matrix.rust }} 128 | 129 | - run: cargo check --workspace --release -vv 130 | 131 | test: 132 | name: Test 133 | runs-on: ${{ matrix.os }} 134 | strategy: 135 | matrix: 136 | build: [ubuntu-nightly, windows-nightly, ubuntu-stable, windows-stable] 137 | include: 138 | - build: ubuntu-nightly 139 | os: ubuntu-latest 140 | rust: nightly 141 | - build: windows-nightly 142 | os: windows-latest 143 | rust: nightly 144 | - build: ubuntu-stable 145 | os: ubuntu-latest 146 | rust: stable 147 | - build: windows-stable 148 | os: windows-latest 149 | rust: stable 150 | 151 | steps: 152 | - uses: actions/checkout@v4 153 | with: 154 | submodules: true 155 | - uses: ./.github/actions/install-rust 156 | with: 157 | toolchain: ${{ matrix.rust }} 158 | - run: cargo test --workspace 159 | - run: cargo test --workspace --no-default-features 160 | 161 | check_nightly: 162 | name: Check on Rust nightly 163 | runs-on: ${{ matrix.os }} 164 | strategy: 165 | matrix: 166 | build: [nightly] 167 | include: 168 | - build: nightly 169 | os: ubuntu-latest 170 | rust: nightly 171 | 172 | steps: 173 | - uses: actions/checkout@v4 174 | with: 175 | submodules: true 176 | - uses: ./.github/actions/install-rust 177 | with: 178 | toolchain: ${{ matrix.rust }} 179 | - run: > 180 | rustup target add 181 | wasm32-wasip1 182 | - run: cargo check --workspace --release -vv --target=wasm32-wasip1 183 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 2 | use crate::OwnedFd; 3 | #[cfg(windows)] 4 | use crate::{OwnedHandle, OwnedSocket}; 5 | 6 | /// A trait to express the ability to consume an object and acquire ownership 7 | /// of its file descriptor. 8 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 9 | #[deprecated( 10 | since = "1.0.0", 11 | note = "`IntoFd` is replaced by `From<...> for OwnedFd` or `Into`" 12 | )] 13 | pub trait IntoFd { 14 | /// Consumes this object, returning the underlying file descriptor. 15 | /// 16 | /// # Example 17 | /// 18 | /// ```rust,no_run 19 | /// use std::fs::File; 20 | /// # use std::io; 21 | /// use io_lifetimes::{IntoFd, OwnedFd}; 22 | /// 23 | /// let f = File::open("foo.txt")?; 24 | /// let owned_fd: OwnedFd = f.into_fd(); 25 | /// # Ok::<(), io::Error>(()) 26 | /// ``` 27 | fn into_fd(self) -> OwnedFd; 28 | } 29 | 30 | /// A trait to express the ability to consume an object and acquire ownership 31 | /// of its handle. 32 | #[cfg(windows)] 33 | #[deprecated( 34 | since = "1.0.0", 35 | note = "`IntoHandle` is replaced by `From<...> for OwnedHandle` or `Into`" 36 | )] 37 | pub trait IntoHandle { 38 | /// Consumes this object, returning the underlying handle. 39 | /// 40 | /// # Example 41 | /// 42 | /// ```rust,no_run 43 | /// use std::fs::File; 44 | /// # use std::io; 45 | /// use io_lifetimes::{IntoHandle, OwnedHandle}; 46 | /// 47 | /// let f = File::open("foo.txt")?; 48 | /// let owned_handle: OwnedHandle = f.into_handle(); 49 | /// # Ok::<(), io::Error>(()) 50 | /// ``` 51 | fn into_handle(self) -> OwnedHandle; 52 | } 53 | 54 | /// A trait to express the ability to consume an object and acquire ownership 55 | /// of its socket. 56 | #[cfg(windows)] 57 | #[deprecated( 58 | since = "1.0.0", 59 | note = "`IntoSocket` is replaced by `From<...> for OwnedSocket` or `Into`" 60 | )] 61 | pub trait IntoSocket { 62 | /// Consumes this object, returning the underlying socket. 63 | fn into_socket(self) -> OwnedSocket; 64 | } 65 | 66 | /// A trait to express the ability to construct an object from a file 67 | /// descriptor. 68 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 69 | pub trait FromFd { 70 | /// Constructs a new instance of `Self` from the given file descriptor. 71 | /// 72 | /// # Example 73 | /// 74 | /// ```rust,no_run 75 | /// use std::fs::File; 76 | /// # use std::io; 77 | /// use io_lifetimes::{FromFd, IntoFd, OwnedFd}; 78 | /// 79 | /// let f = File::open("foo.txt")?; 80 | /// let owned_fd: OwnedFd = f.into_fd(); 81 | /// let f = File::from_fd(owned_fd); 82 | /// # Ok::<(), io::Error>(()) 83 | /// ``` 84 | #[deprecated( 85 | since = "1.0.0", 86 | note = "`FromFd::from_fd` is replaced by `From::from`" 87 | )] 88 | fn from_fd(owned: OwnedFd) -> Self; 89 | 90 | /// Constructs a new instance of `Self` from the given file descriptor 91 | /// converted from `into_owned`. 92 | /// 93 | /// # Example 94 | /// 95 | /// ```rust,no_run 96 | /// use std::fs::File; 97 | /// # use std::io; 98 | /// use io_lifetimes::{FromFd, IntoFd}; 99 | /// 100 | /// let f = File::open("foo.txt")?; 101 | /// let f = File::from_into_fd(f); 102 | /// # Ok::<(), io::Error>(()) 103 | /// ``` 104 | #[inline] 105 | fn from_into_fd>(into_owned: Owned) -> Self 106 | where 107 | Self: Sized + From, 108 | { 109 | Self::from(into_owned.into()) 110 | } 111 | } 112 | 113 | /// A trait to express the ability to construct an object from a handle. 114 | #[cfg(windows)] 115 | pub trait FromHandle { 116 | /// Constructs a new instance of `Self` from the given handle. 117 | /// 118 | /// # Example 119 | /// 120 | /// ```rust,no_run 121 | /// use std::fs::File; 122 | /// # use std::io; 123 | /// use io_lifetimes::{FromHandle, IntoHandle, OwnedHandle}; 124 | /// 125 | /// let f = File::open("foo.txt")?; 126 | /// let owned_handle: OwnedHandle = f.into_handle(); 127 | /// let f = File::from_handle(owned_handle); 128 | /// # Ok::<(), io::Error>(()) 129 | /// ``` 130 | #[deprecated( 131 | since = "1.0.0", 132 | note = "`FromHandle::from_handle` is replaced by `From::from`" 133 | )] 134 | fn from_handle(owned: OwnedHandle) -> Self; 135 | 136 | /// Constructs a new instance of `Self` from the given handle converted 137 | /// from `into_owned`. 138 | /// 139 | /// # Example 140 | /// 141 | /// ```rust,no_run 142 | /// use std::fs::File; 143 | /// # use std::io; 144 | /// use io_lifetimes::{FromHandle, IntoHandle}; 145 | /// 146 | /// let f = File::open("foo.txt")?; 147 | /// let f = File::from_into_handle(f); 148 | /// # Ok::<(), io::Error>(()) 149 | /// ``` 150 | #[inline] 151 | fn from_into_handle>(into_owned: Owned) -> Self 152 | where 153 | Self: Sized + From, 154 | { 155 | Self::from(into_owned.into()) 156 | } 157 | } 158 | 159 | /// A trait to express the ability to construct an object from a socket. 160 | #[cfg(windows)] 161 | pub trait FromSocket { 162 | /// Constructs a new instance of `Self` from the given socket. 163 | #[deprecated( 164 | since = "1.0.0", 165 | note = "`FromSocket::from_socket` is replaced by `From::from`" 166 | )] 167 | fn from_socket(owned: OwnedSocket) -> Self; 168 | 169 | /// Constructs a new instance of `Self` from the given socket converted 170 | /// from `into_owned`. 171 | #[inline] 172 | fn from_into_socket>(into_owned: Owned) -> Self 173 | where 174 | Self: Sized + From, 175 | { 176 | Self::from(into_owned.into()) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /ORG_CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Bytecode Alliance Organizational Code of Conduct (OCoC) 2 | 3 | *Note*: this Code of Conduct pertains to organizations' behavior. Please also see the [Individual Code of Conduct](CODE_OF_CONDUCT.md). 4 | 5 | ## Preamble 6 | 7 | The Bytecode Alliance (BA) welcomes involvement from organizations, 8 | including commercial organizations. This document is an 9 | *organizational* code of conduct, intended particularly to provide 10 | guidance to commercial organizations. It is distinct from the 11 | [Individual Code of Conduct (ICoC)](CODE_OF_CONDUCT.md), and does not 12 | replace the ICoC. This OCoC applies to any group of people acting in 13 | concert as a BA member or as a participant in BA activities, whether 14 | or not that group is formally incorporated in some jurisdiction. 15 | 16 | The code of conduct described below is not a set of rigid rules, and 17 | we did not write it to encompass every conceivable scenario that might 18 | arise. For example, it is theoretically possible there would be times 19 | when asserting patents is in the best interest of the BA community as 20 | a whole. In such instances, consult with the BA, strive for 21 | consensus, and interpret these rules with an intent that is generous 22 | to the community the BA serves. 23 | 24 | While we may revise these guidelines from time to time based on 25 | real-world experience, overall they are based on a simple principle: 26 | 27 | *Bytecode Alliance members should observe the distinction between 28 | public community functions and private functions — especially 29 | commercial ones — and should ensure that the latter support, or at 30 | least do not harm, the former.* 31 | 32 | ## Guidelines 33 | 34 | * **Do not cause confusion about Wasm standards or interoperability.** 35 | 36 | Having an interoperable WebAssembly core is a high priority for 37 | the BA, and members should strive to preserve that core. It is fine 38 | to develop additional non-standard features or APIs, but they 39 | should always be clearly distinguished from the core interoperable 40 | Wasm. 41 | 42 | Treat the WebAssembly name and any BA-associated names with 43 | respect, and follow BA trademark and branding guidelines. If you 44 | distribute a customized version of software originally produced by 45 | the BA, or if you build a product or service using BA-derived 46 | software, use names that clearly distinguish your work from the 47 | original. (You should still provide proper attribution to the 48 | original, of course, wherever such attribution would normally be 49 | given.) 50 | 51 | Further, do not use the WebAssembly name or BA-associated names in 52 | other public namespaces in ways that could cause confusion, e.g., 53 | in company names, names of commercial service offerings, domain 54 | names, publicly-visible social media accounts or online service 55 | accounts, etc. It may sometimes be reasonable, however, to 56 | register such a name in a new namespace and then immediately donate 57 | control of that account to the BA, because that would help the project 58 | maintain its identity. 59 | 60 | For further guidance, see the BA Trademark and Branding Policy 61 | [TODO: create policy, then insert link]. 62 | 63 | * **Do not restrict contributors.** If your company requires 64 | employees or contractors to sign non-compete agreements, those 65 | agreements must not prevent people from participating in the BA or 66 | contributing to related projects. 67 | 68 | This does not mean that all non-compete agreements are incompatible 69 | with this code of conduct. For example, a company may restrict an 70 | employee's ability to solicit the company's customers. However, an 71 | agreement must not block any form of technical or social 72 | participation in BA activities, including but not limited to the 73 | implementation of particular features. 74 | 75 | The accumulation of experience and expertise in individual persons, 76 | who are ultimately free to direct their energy and attention as 77 | they decide, is one of the most important drivers of progress in 78 | open source projects. A company that limits this freedom may hinder 79 | the success of the BA's efforts. 80 | 81 | * **Do not use patents as offensive weapons.** If any BA participant 82 | prevents the adoption or development of BA technologies by 83 | asserting its patents, that undermines the purpose of the 84 | coalition. The collaboration fostered by the BA cannot include 85 | members who act to undermine its work. 86 | 87 | * **Practice responsible disclosure** for security vulnerabilities. 88 | Use designated, non-public reporting channels to disclose technical 89 | vulnerabilities, and give the project a reasonable period to 90 | respond, remediate, and patch. [TODO: optionally include the 91 | security vulnerability reporting URL here.] 92 | 93 | Vulnerability reporters may patch their company's own offerings, as 94 | long as that patching does not significantly delay the reporting of 95 | the vulnerability. Vulnerability information should never be used 96 | for unilateral commercial advantage. Vendors may legitimately 97 | compete on the speed and reliability with which they deploy 98 | security fixes, but withholding vulnerability information damages 99 | everyone in the long run by risking harm to the BA project's 100 | reputation and to the security of all users. 101 | 102 | * **Respect the letter and spirit of open source practice.** While 103 | there is not space to list here all possible aspects of standard 104 | open source practice, some examples will help show what we mean: 105 | 106 | * Abide by all applicable open source license terms. Do not engage 107 | in copyright violation or misattribution of any kind. 108 | 109 | * Do not claim others' ideas or designs as your own. 110 | 111 | * When others engage in publicly visible work (e.g., an upcoming 112 | demo that is coordinated in a public issue tracker), do not 113 | unilaterally announce early releases or early demonstrations of 114 | that work ahead of their schedule in order to secure private 115 | advantage (such as marketplace advantage) for yourself. 116 | 117 | The BA reserves the right to determine what constitutes good open 118 | source practices and to take action as it deems appropriate to 119 | encourage, and if necessary enforce, such practices. 120 | 121 | ## Enforcement 122 | 123 | Instances of organizational behavior in violation of the OCoC may 124 | be reported by contacting the Bytecode Alliance CoC team at 125 | [report@bytecodealliance.org](mailto:report@bytecodealliance.org). The 126 | CoC team will review and investigate all complaints, and will respond 127 | in a way that it deems appropriate to the circumstances. The CoC team 128 | is obligated to maintain confidentiality with regard to the reporter of 129 | an incident. Further details of specific enforcement policies may be 130 | posted separately. 131 | 132 | When the BA deems an organization in violation of this OCoC, the BA 133 | will, at its sole discretion, determine what action to take. The BA 134 | will decide what type, degree, and duration of corrective action is 135 | needed, if any, before a violating organization can be considered for 136 | membership (if it was not already a member) or can have its membership 137 | reinstated (if it was a member and the BA canceled its membership due 138 | to the violation). 139 | 140 | In practice, the BA's first approach will be to start a conversation, 141 | with punitive enforcement used only as a last resort. Violations 142 | often turn out to be unintentional and swiftly correctable with all 143 | parties acting in good faith. 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

io-lifetimes

3 | 4 |

5 | A low-level I/O ownership and borrowing library 6 |

7 | 8 |

9 | Github Actions CI Status 10 | crates.io page 11 | docs.rs docs 12 |

13 |
14 | 15 | This library introduces `OwnedFd`, `BorrowedFd`, and supporting types and 16 | traits, and corresponding features for Windows, which implement safe owning 17 | and borrowing I/O lifetime patterns. 18 | 19 | This is associated with [RFC 3128], the I/O Safety RFC, which is now merged. 20 | Work is now underway to move the `OwnedFd` and `BorrowedFd` types and `AsFd` 21 | trait developed here into `std`. 22 | 23 | For a quick taste, check out the code examples: 24 | 25 | - [hello], a basic demo of this API, doing low-level I/O manually, using the 26 | [provided example FFI bindings] 27 | - [easy-conversions], demonstrating the `from_into` convenience feature for 28 | converting from an `impl Into*` into an `impl From*`. 29 | - [portable-views], demonstrating the convenience feature which allows one 30 | to temporarily "view" a file descriptor as any owning type such as `File` 31 | - [flexible-apis], demonstrating how to write library APIs that accept 32 | untyped I/O resources. 33 | - [owning-wrapper], demonstrating how to implement a type which wraps an 34 | `Owned*` type. 35 | 36 | [hello]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/hello.rs 37 | [easy-conversions]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/easy-conversions.rs 38 | [portable-views]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/portable-views.rs 39 | [flexible-apis]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/flexible-apis.rs 40 | [owning-wrapper]: https://github.com/sunfishcode/io-lifetimes/blob/main/examples/owning-wrapper.rs 41 | [provided example FFI bindings]: https://github.com/sunfishcode/io-lifetimes/blob/main/src/example_ffi.rs 42 | 43 | The core of the API is very simple, and consists of two main types and three 44 | main traits: 45 | 46 | ```rust 47 | pub struct BorrowedFd<'fd> { ... } 48 | pub struct OwnedFd { ... } 49 | 50 | pub trait AsFd { ... } 51 | pub trait IntoFd { ... } 52 | pub trait FromFd { ... } 53 | 54 | impl AsRawFd for BorrowedFd<'_> { ... } 55 | impl AsRawFd for OwnedFd { ... } 56 | impl IntoRawFd for OwnedFd { ... } 57 | impl FromRawFd for OwnedFd { ... } 58 | 59 | impl Drop for OwnedFd { ... } 60 | 61 | impl AsFd for BorrowedFd<'_> { ... } 62 | impl AsFd for OwnedFd { ... } 63 | impl IntoFd for OwnedFd { ... } 64 | impl FromFd for OwnedFd { ... } 65 | ``` 66 | 67 | On Windows, there are `Handle` and `Socket` versions of every `Fd` thing, and 68 | a special `HandleOrInvalid` type to cope with inconsistent error reporting 69 | in the Windows API. 70 | 71 | ## The magic of transparency 72 | 73 | Here's the fun part. `BorrowedFd` and `OwnedFd` are `repr(transparent)` and 74 | hold `RawFd` values, and `Option` and `Option` are 75 | FFI-safe (on Rust >= 1.63), so they can all be used in FFI [directly]: 76 | 77 | [directly]: https://github.com/sunfishcode/io-lifetimes/blob/main/src/example_ffi.rs 78 | 79 | ```rust 80 | extern "C" { 81 | pub fn open(pathname: *const c_char, flags: c_int, ...) -> Option; 82 | pub fn read(fd: BorrowedFd<'_>, ptr: *mut c_void, size: size_t) -> ssize_t; 83 | pub fn write(fd: BorrowedFd<'_>, ptr: *const c_void, size: size_t) -> ssize_t; 84 | pub fn close(fd: OwnedFd) -> c_int; 85 | } 86 | ``` 87 | 88 | With bindings like this, users never have to touch `RawFd` values. Of course, 89 | not all code will do this, but it is a fun feature for code that can. This 90 | is what motivates having `BorrowedFd` instead of just using `&OwnedFd`. 91 | 92 | Note the use of `Option` as the return value of `open`, representing 93 | the fact that it can either succeed or fail. 94 | 95 | ## I/O Safety in Rust 96 | 97 | I/O Safety feature is stablized in Rust 1.63. With this version or later, 98 | io-lifetimes will use and re-export the standard-library types and traits. With 99 | older versions, io-lifetimes defines its own copy of these types and traits. 100 | 101 | io-lifetimes also includes several features which are not (yet?) in std, 102 | including the portability traits `AsFilelike`/`AsSocketlike`/etc., the 103 | `from_into_*` functions in the `From*` traits, and [views]. 104 | 105 | [views]: https://docs.rs/io-lifetimes/*/io_lifetimes/views/index.html 106 | 107 | ## Prior Art 108 | 109 | There are several similar crates: [fd](https://crates.io/crates/fd), 110 | [filedesc](https://crates.io/crates/filedesc), 111 | [filedescriptor](https://crates.io/crates/filedescriptor), 112 | [owned-fd](https://crates.io/crates/owned-fd), and 113 | [unsafe-io](https://crates.io/crates/unsafe-io). 114 | 115 | Some of these provide additional features such as the ability to create pipes 116 | or sockets, to get and set flags, and to do read and write operations. 117 | io-lifetimes omits these features, leaving them to to be provided as separate 118 | layers on top. 119 | 120 | Most of these crates provide ways to duplicate a file descriptor. io-lifetimes 121 | currently treats this as another feature that can be provided by a layer on 122 | top, though if there are use cases where this is a common operation, it could 123 | be added. 124 | 125 | io-lifetimes's distinguishing features are its use of `repr(transparent)` 126 | to support direct FFI usage, niche optimizations so `Option` can support direct 127 | FFI usafe as well (on Rust >= 1.63), lifetime-aware `As*`/`Into*`/`From*` 128 | traits which leverage Rust's lifetime system and allow safe and checked 129 | `from_*` and `as_*`/`into_*` functions, and powerful convenience features 130 | enabled by its underlying safety. 131 | 132 | io-lifetimes also has full Windows support, as well as Unix/Windows 133 | portability abstractions, covering both file-like and socket-like types. 134 | 135 | io-lifetimes's [`OwnedFd`] type is similar to 136 | [fd](https://crates.io/crates/fd)'s 137 | [`FileDesc`](https://docs.rs/fd/0.2.3/fd/struct.FileDesc.html). io-lifetimes 138 | doesn't have a `close_on_drop` parameter, and instead uses [`OwnedFd`] and 139 | [`BorrowedFd`] to represent dropping and non-dropping handles, respectively, in 140 | a way that is checked at compile time rather than runtime. 141 | 142 | io-lifetimes's [`OwnedFd`] type is also similar to 143 | [filedesc](https://crates.io/crates/filedesc)'s 144 | [`FileDesc`](https://docs.rs/filedesc/0.3.0/filedesc/struct.FileDesc.html) 145 | io-lifetimes's `OwnedFd` reserves the value -1, so it doesn't need to test for 146 | `-1` in its `Drop`, and `Option` (on Rust >= 1.63) is the same size 147 | as `FileDesc`. 148 | 149 | io-lifetimes's [`OwnedFd`] type is also similar to 150 | [owned-fd](https://crates.io/crates/owned-fd)'s 151 | [`OwnedFd`](https://docs.rs/owned-fd/0.1.0/owned_fd/struct.OwnedFd.html). 152 | io-lifetimes doesn't implement `Clone`, because duplicating a file descriptor 153 | can fail due to OS process limits, while `Clone` is an infallible interface. 154 | 155 | io-lifetimes's [`BorrowedFd`] is similar to 156 | [owned-fd](https://crates.io/crates/owned-fd)'s 157 | [`FdRef`](https://docs.rs/owned-fd/0.1.0/owned_fd/struct.FdRef.html), except it 158 | uses a lifetime parameter and `PhantomData` rather than transmuting a raw file 159 | descriptor value into a reference value. 160 | 161 | io-lifetimes's convenience features are similar to those of 162 | [unsafe-io](https://crates.io/crates/unsafe-io), but io-lifetimes is built on 163 | its own `As*`/`Into*`/`From*` traits, rather than extending 164 | `AsRaw*`/`IntoRaw*`/`FromRaw*` with 165 | [`OwnsRaw`](https://docs.rs/unsafe-io/0.6.9/unsafe_io/trait.OwnsRaw.html), so 166 | they're simpler and safer to use. io-lifetimes doesn't include unsafe-io's 167 | `*ReadWrite*` or `*HandleOrSocket*` abstractions, and leaves these as features 168 | to be provided by separate layers on top. 169 | 170 | ## Minimum Supported Rust Version (MSRV) 171 | 172 | This crate currently works on Rust 1.70. This policy may change in the future, 173 | in minor version releases, so users using a fixed version of Rust should pin to 174 | a specific version of this crate. 175 | 176 | [`OwnedFd`]: https://doc.rust-lang.org/stable/std/os/fd/struct.OwnedFd.html 177 | [`BorrowedFd`]: https://doc.rust-lang.org/stable/std/os/fd/struct.BorrowedFd.html 178 | [RFC 3128]: https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md 179 | -------------------------------------------------------------------------------- /src/raw.rs: -------------------------------------------------------------------------------- 1 | //! Portability abstractions over `Raw*`. 2 | //! 3 | //! On Unix, "everything is a file descriptor". On Windows, file/pipe/process 4 | //! handles are distinct from socket descriptors. This file provides a minimal 5 | //! layer of portability over this difference. 6 | 7 | #[cfg(target_os = "hermit")] 8 | use std::os::hermit::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; 9 | #[cfg(unix)] 10 | use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; 11 | #[cfg(target_os = "wasi")] 12 | use std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; 13 | #[cfg(windows)] 14 | use std::os::windows::io::{ 15 | AsRawHandle, AsRawSocket, FromRawHandle, FromRawSocket, IntoRawHandle, IntoRawSocket, 16 | RawHandle, RawSocket, 17 | }; 18 | 19 | /// A raw filelike object. 20 | /// 21 | /// This is a portability abstraction over Unix-like [`RawFd`] and 22 | /// Windows' `RawHandle`. 23 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 24 | pub type RawFilelike = RawFd; 25 | 26 | /// A raw filelike object. 27 | /// 28 | /// This is a portability abstraction over Unix-like `RawFd` and 29 | /// Windows' [`RawHandle`]. 30 | #[cfg(windows)] 31 | pub type RawFilelike = RawHandle; 32 | 33 | /// A raw socketlike object. 34 | /// 35 | /// This is a portability abstraction over Unix-like [`RawFd`] and 36 | /// Windows' `RawSocket`. 37 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 38 | pub type RawSocketlike = RawFd; 39 | 40 | /// A raw socketlike object. 41 | /// 42 | /// This is a portability abstraction over Unix-like `RawFd` and 43 | /// Windows' [`RawSocket`]. 44 | #[cfg(windows)] 45 | pub type RawSocketlike = RawSocket; 46 | 47 | /// A portable trait to obtain the raw value of an underlying filelike object. 48 | /// 49 | /// This is a portability abstraction over Unix-like [`AsRawFd`] and Windows' 50 | /// `AsRawHandle`. 51 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 52 | pub trait AsRawFilelike: AsRawFd { 53 | /// Returns the raw value. 54 | fn as_raw_filelike(&self) -> RawFilelike; 55 | } 56 | 57 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 58 | impl AsRawFilelike for T { 59 | #[inline] 60 | fn as_raw_filelike(&self) -> RawFilelike { 61 | self.as_raw_fd() 62 | } 63 | } 64 | 65 | /// This is a portability abstraction over Unix-like `AsRawFd` and Windows' 66 | /// [`AsRawHandle`]. 67 | #[cfg(windows)] 68 | pub trait AsRawFilelike: AsRawHandle { 69 | /// Returns the raw value. 70 | fn as_raw_filelike(&self) -> RawFilelike; 71 | } 72 | 73 | #[cfg(windows)] 74 | impl AsRawFilelike for T { 75 | #[inline] 76 | fn as_raw_filelike(&self) -> RawFilelike { 77 | self.as_raw_handle() 78 | } 79 | } 80 | 81 | /// This is a portability abstraction over Unix-like [`AsRawFd`] and Windows' 82 | /// `AsRawSocket`. 83 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 84 | pub trait AsRawSocketlike: AsRawFd { 85 | /// Returns the raw value. 86 | fn as_raw_socketlike(&self) -> RawSocketlike; 87 | } 88 | 89 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 90 | impl AsRawSocketlike for T { 91 | #[inline] 92 | fn as_raw_socketlike(&self) -> RawSocketlike { 93 | self.as_raw_fd() 94 | } 95 | } 96 | 97 | /// This is a portability abstraction over Unix-like `AsRawFd` and Windows' 98 | /// [`AsRawSocket`]. 99 | #[cfg(windows)] 100 | pub trait AsRawSocketlike: AsRawSocket { 101 | /// Returns the raw value. 102 | fn as_raw_socketlike(&self) -> RawSocketlike; 103 | } 104 | 105 | #[cfg(windows)] 106 | impl AsRawSocketlike for T { 107 | #[inline] 108 | fn as_raw_socketlike(&self) -> RawSocketlike { 109 | self.as_raw_socket() 110 | } 111 | } 112 | 113 | /// This is a portability abstraction over Unix-like [`IntoRawFd`] and Windows' 114 | /// `IntoRawHandle`. 115 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 116 | pub trait IntoRawFilelike: IntoRawFd { 117 | /// Returns the raw value. 118 | fn into_raw_filelike(self) -> RawFilelike; 119 | } 120 | 121 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 122 | impl IntoRawFilelike for T { 123 | #[inline] 124 | fn into_raw_filelike(self) -> RawFilelike { 125 | self.into_raw_fd() 126 | } 127 | } 128 | 129 | /// This is a portability abstraction over Unix-like `IntoRawFd` and Windows' 130 | /// [`IntoRawHandle`]. 131 | #[cfg(windows)] 132 | pub trait IntoRawFilelike: IntoRawHandle { 133 | /// Returns the raw value. 134 | fn into_raw_filelike(self) -> RawFilelike; 135 | } 136 | 137 | #[cfg(windows)] 138 | impl IntoRawFilelike for T { 139 | #[inline] 140 | fn into_raw_filelike(self) -> RawFilelike { 141 | self.into_raw_handle() 142 | } 143 | } 144 | 145 | /// This is a portability abstraction over Unix-like [`IntoRawFd`] and Windows' 146 | /// `IntoRawSocket`. 147 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 148 | pub trait IntoRawSocketlike: IntoRawFd { 149 | /// Returns the raw value. 150 | fn into_raw_socketlike(self) -> RawSocketlike; 151 | } 152 | 153 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 154 | impl IntoRawSocketlike for T { 155 | #[inline] 156 | fn into_raw_socketlike(self) -> RawSocketlike { 157 | self.into_raw_fd() 158 | } 159 | } 160 | 161 | /// This is a portability abstraction over Unix-like `IntoRawFd` and Windows' 162 | /// [`IntoRawSocket`]. 163 | #[cfg(windows)] 164 | pub trait IntoRawSocketlike: IntoRawSocket { 165 | /// Returns the raw value. 166 | fn into_raw_socketlike(self) -> RawSocketlike; 167 | } 168 | 169 | #[cfg(windows)] 170 | impl IntoRawSocketlike for T { 171 | #[inline] 172 | fn into_raw_socketlike(self) -> RawSocketlike { 173 | self.into_raw_socket() 174 | } 175 | } 176 | 177 | /// This is a portability abstraction over Unix-like [`FromRawFd`] and Windows' 178 | /// `FromRawHandle`. 179 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 180 | pub trait FromRawFilelike: FromRawFd { 181 | /// Constructs `Self` from the raw value. 182 | /// 183 | /// # Safety 184 | /// 185 | /// This is `unsafe` for the same reason as [`from_raw_fd`] and 186 | /// [`from_raw_handle`]. 187 | /// 188 | /// [`from_raw_fd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html#tymethod.from_raw_fd 189 | /// [`from_raw_handle`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle 190 | unsafe fn from_raw_filelike(raw: RawFilelike) -> Self; 191 | } 192 | 193 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 194 | impl FromRawFilelike for T { 195 | #[inline] 196 | unsafe fn from_raw_filelike(raw: RawFilelike) -> Self { 197 | Self::from_raw_fd(raw) 198 | } 199 | } 200 | 201 | /// This is a portability abstraction over Unix-like `FromRawFd` and Windows' 202 | /// [`FromRawHandle`]. 203 | #[cfg(windows)] 204 | pub trait FromRawFilelike: FromRawHandle { 205 | /// Constructs `Self` from the raw value. 206 | unsafe fn from_raw_filelike(raw: RawFilelike) -> Self; 207 | } 208 | 209 | #[cfg(windows)] 210 | impl FromRawFilelike for T { 211 | #[inline] 212 | unsafe fn from_raw_filelike(raw: RawFilelike) -> Self { 213 | Self::from_raw_handle(raw) 214 | } 215 | } 216 | 217 | /// This is a portability abstraction over Unix-like [`FromRawFd`] and Windows' 218 | /// `FromRawSocket`. 219 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 220 | pub trait FromRawSocketlike: FromRawFd { 221 | /// Constructs `Self` from the raw value. 222 | /// 223 | /// # Safety 224 | /// 225 | /// This is `unsafe` for the same reason as [`from_raw_fd`] and 226 | /// [`from_raw_socket`]. 227 | /// 228 | /// [`from_raw_fd`]: https://doc.rust-lang.org/stable/std/os/fd/trait.FromRawFd.html#tymethod.from_raw_fd 229 | /// [`from_raw_socket`]: https://doc.rust-lang.org/stable/std/os/windows/io/trait.FromRawSocket.html#tymethod.from_raw_socket 230 | unsafe fn from_raw_socketlike(raw: RawSocketlike) -> Self; 231 | } 232 | 233 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 234 | impl FromRawSocketlike for T { 235 | #[inline] 236 | unsafe fn from_raw_socketlike(raw: RawSocketlike) -> Self { 237 | Self::from_raw_fd(raw) 238 | } 239 | } 240 | 241 | /// This is a portability abstraction over Unix-like `FromRawFd` and Windows' 242 | /// [`FromRawSocket`]. 243 | #[cfg(windows)] 244 | pub trait FromRawSocketlike: FromRawSocket { 245 | /// Constructs `Self` from the raw value. 246 | unsafe fn from_raw_socketlike(raw: RawSocketlike) -> Self; 247 | } 248 | 249 | #[cfg(windows)] 250 | impl FromRawSocketlike for T { 251 | #[inline] 252 | unsafe fn from_raw_socketlike(raw: RawSocketlike) -> Self { 253 | Self::from_raw_socket(raw) 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/views.rs: -------------------------------------------------------------------------------- 1 | //! Typed views using temporary objects. 2 | //! 3 | //! This module defines the return types for [`AsFilelike::as_filelike_view`] 4 | //! and [`AsSocketlike::as_socketlike_view`]. 5 | //! 6 | //! [`AsSocketlike::as_socketlike_view`]: crate::AsSocketlike::as_socketlike_view 7 | 8 | use crate::raw::{ 9 | AsRawFilelike, AsRawSocketlike, FromRawFilelike, FromRawSocketlike, IntoRawFilelike, 10 | IntoRawSocketlike, RawFilelike, RawSocketlike, 11 | }; 12 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 13 | use crate::OwnedFd; 14 | use crate::{ 15 | AsFilelike, AsSocketlike, FromFilelike, FromSocketlike, IntoFilelike, IntoSocketlike, 16 | OwnedFilelike, OwnedSocketlike, 17 | }; 18 | #[cfg(windows)] 19 | use crate::{OwnedHandle, OwnedSocket}; 20 | use std::fmt; 21 | use std::marker::PhantomData; 22 | use std::mem::ManuallyDrop; 23 | use std::ops::Deref; 24 | 25 | /// Declare that a type is safe to use in a [`FilelikeView`]. 26 | /// 27 | /// # Safety 28 | /// 29 | /// Types implementing this trait declare that if they are constructed with 30 | /// [`FromFilelike`] and consumed with [`IntoFilelike`], their `IntoFilelike` 31 | /// will return the same `OwnedFd` value that was passed to their 32 | /// `FromFilelike`. 33 | pub unsafe trait FilelikeViewType: FromFilelike + IntoFilelike {} 34 | 35 | /// Declare that a type is safe to use in a [`SocketlikeView`]. 36 | /// 37 | /// # Safety 38 | /// 39 | /// Types implementing this trait declare that if they are constructed with 40 | /// [`FromSocketlike`] and consumed with [`IntoSocketlike`], their 41 | /// `IntoSocketlike` will return the same `OwnedFd` value that was passed to 42 | /// their `FromSocketlike`. 43 | pub unsafe trait SocketlikeViewType: FromSocketlike + IntoSocketlike {} 44 | 45 | /// A non-owning view of a resource which dereferences to a `&Target` or 46 | /// `&mut Target`. These are returned by [`AsFilelike::as_filelike_view`]. 47 | pub struct FilelikeView<'filelike, Target: FilelikeViewType> { 48 | /// The value to dereference to. This is a `ManuallyDrop` so that we can 49 | /// consume it in our `Drop` impl. 50 | target: ManuallyDrop, 51 | 52 | /// `FilelikeViewType` implementors guarantee that their `Into` 53 | /// returns the same fd as their `From` gave them. This field 54 | /// allows us to verify this. 55 | #[cfg(debug_assertions)] 56 | orig: RawFilelike, 57 | 58 | /// This field exists because we don't otherwise explicitly use 59 | /// `'filelike`. 60 | _phantom: PhantomData<&'filelike OwnedFilelike>, 61 | } 62 | 63 | /// A non-owning view of a resource which dereferences to a `&Target` or 64 | /// `&mut Target`. These are returned by [`AsSocketlike::as_socketlike_view`]. 65 | pub struct SocketlikeView<'socketlike, Target: SocketlikeViewType> { 66 | /// The value to dereference to. This is a `ManuallyDrop` so that we can 67 | /// consume it in our `Drop` impl. 68 | target: ManuallyDrop, 69 | 70 | /// `SocketlikeViewType` implementors guarantee that their `Into` 71 | /// returns the same fd as their `From` gave them. This field 72 | /// allows us to verify this. 73 | #[cfg(debug_assertions)] 74 | orig: RawSocketlike, 75 | 76 | /// This field exists because we don't otherwise explicitly use 77 | /// `'socketlike`. 78 | _phantom: PhantomData<&'socketlike OwnedSocketlike>, 79 | } 80 | 81 | impl FilelikeView<'_, Target> { 82 | /// Construct a temporary `Target` and wrap it in a `FilelikeView` object. 83 | #[inline] 84 | pub(crate) fn new(filelike: &T) -> Self { 85 | // Safety: The returned `FilelikeView` is scoped to the lifetime of 86 | // `filelike`, which we've borrowed here, so the view won't outlive 87 | // the object it's borrowed from. 88 | unsafe { Self::view_raw(filelike.as_filelike().as_raw_filelike()) } 89 | } 90 | 91 | /// Construct a temporary `Target` from raw and wrap it in a `FilelikeView` 92 | /// object. 93 | /// 94 | /// # Safety 95 | /// 96 | /// `raw` must be a valid raw filelike referencing a resource that outlives 97 | /// the resulting view. 98 | #[inline] 99 | pub unsafe fn view_raw(raw: RawFilelike) -> Self { 100 | let owned = OwnedFilelike::from_raw_filelike(raw); 101 | Self { 102 | target: ManuallyDrop::new(Target::from_filelike(owned)), 103 | #[cfg(debug_assertions)] 104 | orig: raw, 105 | _phantom: PhantomData, 106 | } 107 | } 108 | } 109 | 110 | impl SocketlikeView<'_, Target> { 111 | /// Construct a temporary `Target` and wrap it in a `SocketlikeView` 112 | /// object. 113 | #[inline] 114 | pub(crate) fn new(socketlike: &T) -> Self { 115 | // Safety: The returned `SocketlikeView` is scoped to the lifetime of 116 | // `socketlike`, which we've borrowed here, so the view won't outlive 117 | // the object it's borrowed from. 118 | unsafe { Self::view_raw(socketlike.as_socketlike().as_raw_socketlike()) } 119 | } 120 | 121 | /// Construct a temporary `Target` from raw and wrap it in a 122 | /// `SocketlikeView` object. 123 | /// 124 | /// # Safety 125 | /// 126 | /// `raw` must be a valid raw socketlike referencing a resource that 127 | /// outlives the resulting view. 128 | #[inline] 129 | pub unsafe fn view_raw(raw: RawSocketlike) -> Self { 130 | let owned = OwnedSocketlike::from_raw_socketlike(raw); 131 | Self { 132 | target: ManuallyDrop::new(Target::from_socketlike(owned)), 133 | #[cfg(debug_assertions)] 134 | orig: raw, 135 | _phantom: PhantomData, 136 | } 137 | } 138 | } 139 | 140 | impl Deref for FilelikeView<'_, Target> { 141 | type Target = Target; 142 | 143 | #[inline] 144 | fn deref(&self) -> &Self::Target { 145 | &self.target 146 | } 147 | } 148 | 149 | impl Deref for SocketlikeView<'_, Target> { 150 | type Target = Target; 151 | 152 | #[inline] 153 | fn deref(&self) -> &Self::Target { 154 | &self.target 155 | } 156 | } 157 | 158 | impl Drop for FilelikeView<'_, Target> { 159 | #[inline] 160 | fn drop(&mut self) { 161 | // Use `Into*` to consume `self.target` without freeing its resource. 162 | // 163 | // Safety: Using `ManuallyDrop::take` requires us to ensure that 164 | // `self.target` is not used again. We don't use it again here, and 165 | // this is the `drop` function, so we know it's not used afterward. 166 | let _raw = unsafe { ManuallyDrop::take(&mut self.target) } 167 | .into_filelike() 168 | .into_raw_filelike(); 169 | 170 | #[cfg(debug_assertions)] 171 | debug_assert_eq!(self.orig, _raw); 172 | } 173 | } 174 | 175 | impl Drop for SocketlikeView<'_, Target> { 176 | #[inline] 177 | fn drop(&mut self) { 178 | // Use `Into*` to consume `self.target` without freeing its resource. 179 | // 180 | // Safety: Using `ManuallyDrop::take` requires us to ensure that 181 | // `self.target` is not used again. We don't use it again here, and 182 | // this is the `drop` function, so we know it's not used afterward. 183 | let _raw = unsafe { ManuallyDrop::take(&mut self.target) } 184 | .into_socketlike() 185 | .into_raw_socketlike(); 186 | 187 | #[cfg(debug_assertions)] 188 | debug_assert_eq!(self.orig, _raw); 189 | } 190 | } 191 | 192 | impl fmt::Debug for FilelikeView<'_, Target> { 193 | #[allow(clippy::missing_inline_in_public_items)] 194 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 195 | f.debug_struct("FilelikeView") 196 | .field("target", &*self) 197 | .finish() 198 | } 199 | } 200 | 201 | impl fmt::Debug for SocketlikeView<'_, Target> { 202 | #[allow(clippy::missing_inline_in_public_items)] 203 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 204 | f.debug_struct("SocketlikeView") 205 | .field("target", &*self) 206 | .finish() 207 | } 208 | } 209 | 210 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 211 | unsafe impl FilelikeViewType for OwnedFd {} 212 | #[cfg(windows)] 213 | unsafe impl FilelikeViewType for OwnedHandle {} 214 | #[cfg(windows)] 215 | unsafe impl SocketlikeViewType for OwnedSocket {} 216 | unsafe impl FilelikeViewType for std::fs::File {} 217 | unsafe impl SocketlikeViewType for std::net::TcpStream {} 218 | unsafe impl SocketlikeViewType for std::net::TcpListener {} 219 | unsafe impl SocketlikeViewType for std::net::UdpSocket {} 220 | #[cfg(unix)] 221 | unsafe impl SocketlikeViewType for std::os::unix::net::UnixStream {} 222 | #[cfg(unix)] 223 | unsafe impl SocketlikeViewType for std::os::unix::net::UnixListener {} 224 | 225 | #[cfg(unix)] 226 | unsafe impl SocketlikeViewType for std::os::unix::net::UnixDatagram {} 227 | #[cfg(not(any(target_os = "wasi", target_os = "hermit")))] 228 | #[cfg(feature = "os_pipe")] 229 | unsafe impl FilelikeViewType for os_pipe::PipeWriter {} 230 | #[cfg(not(any(target_os = "wasi", target_os = "hermit")))] 231 | #[cfg(feature = "os_pipe")] 232 | unsafe impl FilelikeViewType for os_pipe::PipeReader {} 233 | 234 | #[cfg(not(any(target_os = "wasi", target_os = "hermit")))] 235 | #[cfg(feature = "socket2")] 236 | unsafe impl SocketlikeViewType for socket2::Socket {} 237 | 238 | #[cfg(not(any(target_os = "wasi", target_os = "hermit")))] 239 | #[cfg(feature = "async-std")] 240 | unsafe impl SocketlikeViewType for async_std::net::TcpStream {} 241 | #[cfg(not(any(target_os = "wasi", target_os = "hermit")))] 242 | #[cfg(feature = "async-std")] 243 | unsafe impl SocketlikeViewType for async_std::net::TcpListener {} 244 | #[cfg(not(any(target_os = "wasi", target_os = "hermit")))] 245 | #[cfg(feature = "async-std")] 246 | unsafe impl SocketlikeViewType for async_std::net::UdpSocket {} 247 | #[cfg(unix)] 248 | #[cfg(feature = "async-std")] 249 | unsafe impl SocketlikeViewType for async_std::os::unix::net::UnixStream {} 250 | #[cfg(unix)] 251 | #[cfg(feature = "async-std")] 252 | unsafe impl SocketlikeViewType for async_std::os::unix::net::UnixListener {} 253 | #[cfg(unix)] 254 | #[cfg(feature = "async-std")] 255 | unsafe impl SocketlikeViewType for async_std::os::unix::net::UnixDatagram {} 256 | 257 | #[cfg(feature = "mio")] 258 | unsafe impl SocketlikeViewType for mio::net::TcpStream {} 259 | #[cfg(feature = "mio")] 260 | unsafe impl SocketlikeViewType for mio::net::TcpListener {} 261 | #[cfg(feature = "mio")] 262 | unsafe impl SocketlikeViewType for mio::net::UdpSocket {} 263 | #[cfg(unix)] 264 | #[cfg(feature = "mio")] 265 | unsafe impl SocketlikeViewType for mio::net::UnixDatagram {} 266 | #[cfg(unix)] 267 | #[cfg(feature = "mio")] 268 | unsafe impl SocketlikeViewType for mio::net::UnixListener {} 269 | #[cfg(unix)] 270 | #[cfg(feature = "mio")] 271 | unsafe impl SocketlikeViewType for mio::net::UnixStream {} 272 | #[cfg(unix)] 273 | #[cfg(feature = "mio")] 274 | unsafe impl FilelikeViewType for mio::unix::pipe::Receiver {} 275 | #[cfg(unix)] 276 | #[cfg(feature = "mio")] 277 | unsafe impl FilelikeViewType for mio::unix::pipe::Sender {} 278 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-Apache-2.0_WITH_LLVM-exception: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | 205 | --- LLVM Exceptions to the Apache 2.0 License ---- 206 | 207 | As an exception, if, as a result of your compiling your source code, portions 208 | of this Software are embedded into an Object form of such source code, you 209 | may redistribute such embedded portions in such Object form without complying 210 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 211 | 212 | In addition, if you combine or link compiled forms of this Software with 213 | software that is licensed under the GPLv2 ("Combined Software") and if a 214 | court of competent jurisdiction determines that the patent provision (Section 215 | 3), the indemnity provision (Section 9) or other Section of the License 216 | conflicts with the conditions of the GPLv2, you may retroactively and 217 | prospectively choose to deem waived or otherwise exclude such Section(s) of 218 | the License, but only in their entirety and only with respect to the Combined 219 | Software. 220 | 221 | -------------------------------------------------------------------------------- /src/portability.rs: -------------------------------------------------------------------------------- 1 | //! Portability abstractions over `Owned*` and `Borrowed*`. 2 | //! 3 | //! On Unix, "everything is a file descriptor". On Windows, file/pipe/process 4 | //! handles are distinct from socket descriptors. This file provides a minimal 5 | //! layer of portability over this difference. 6 | 7 | use crate::views::{FilelikeView, FilelikeViewType, SocketlikeView, SocketlikeViewType}; 8 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 9 | use crate::{AsFd, BorrowedFd, OwnedFd}; 10 | #[cfg(windows)] 11 | use crate::{AsHandle, AsSocket, BorrowedHandle, BorrowedSocket, OwnedHandle, OwnedSocket}; 12 | 13 | /// A reference to a filelike object. 14 | /// 15 | /// This is a portability abstraction over Unix-like [`BorrowedFd`] and 16 | /// Windows' `BorrowedHandle`. 17 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 18 | pub type BorrowedFilelike<'filelike> = BorrowedFd<'filelike>; 19 | 20 | /// A reference to a filelike object. 21 | /// 22 | /// This is a portability abstraction over Unix-like `BorrowedFd` and 23 | /// Windows' [`BorrowedHandle`]. 24 | #[cfg(windows)] 25 | pub type BorrowedFilelike<'filelike> = BorrowedHandle<'filelike>; 26 | 27 | /// A reference to a socketlike object. 28 | /// 29 | /// This is a portability abstraction over Unix-like [`BorrowedFd`] and 30 | /// Windows' `BorrowedSocket`. 31 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 32 | pub type BorrowedSocketlike<'socketlike> = BorrowedFd<'socketlike>; 33 | 34 | /// A reference to a socketlike object. 35 | /// 36 | /// This is a portability abstraction over Unix-like `BorrowedFd` and 37 | /// Windows' [`BorrowedSocket`]. 38 | #[cfg(windows)] 39 | pub type BorrowedSocketlike<'socketlike> = BorrowedSocket<'socketlike>; 40 | 41 | /// An owned filelike object. 42 | /// 43 | /// This is a portability abstraction over Unix-like [`OwnedFd`] and 44 | /// Windows' `OwnedHandle`. 45 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 46 | pub type OwnedFilelike = OwnedFd; 47 | 48 | /// An owned filelike object. 49 | /// 50 | /// This is a portability abstraction over Unix-like `OwnedFd` and 51 | /// Windows' [`OwnedHandle`]. 52 | #[cfg(windows)] 53 | pub type OwnedFilelike = OwnedHandle; 54 | 55 | /// An owned socketlike object. 56 | /// 57 | /// This is a portability abstraction over Unix-like [`OwnedFd`] and 58 | /// Windows' `OwnedSocket`. 59 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 60 | pub type OwnedSocketlike = OwnedFd; 61 | 62 | /// An owned socketlike object. 63 | /// 64 | /// This is a portability abstraction over Unix-like `OwnedFd` and 65 | /// Windows' [`OwnedSocket`]. 66 | #[cfg(windows)] 67 | pub type OwnedSocketlike = OwnedSocket; 68 | 69 | /// A portable trait to borrow a reference from an underlying filelike object. 70 | /// 71 | /// This is a portability abstraction over Unix-like [`AsFd`] and Windows' 72 | /// `AsHandle`. It also provides the `as_filelike_view` convenience function 73 | /// providing typed views. 74 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 75 | pub trait AsFilelike: AsFd { 76 | /// Borrows the reference. 77 | /// 78 | /// # Example 79 | /// 80 | /// ```rust,no_run 81 | /// use std::fs::File; 82 | /// # use std::io; 83 | /// use io_lifetimes::{AsFilelike, BorrowedFilelike}; 84 | /// 85 | /// let mut f = File::open("foo.txt")?; 86 | /// let borrowed_filelike: BorrowedFilelike<'_> = f.as_filelike(); 87 | /// # Ok::<(), io::Error>(()) 88 | /// ``` 89 | fn as_filelike(&self) -> BorrowedFilelike<'_>; 90 | 91 | /// Return a borrowing view of a resource which dereferences to a 92 | /// `&Target`. 93 | /// 94 | /// Note that [`Read`] or [`Write`] require `&mut Target`, but in some 95 | /// cases, such as [`File`], `Read` and `Write` are implemented for 96 | /// `&Target` in addition to `Target`, and you can get a `&mut &Target` 97 | /// by doing `&*` on the resuting view, like this: 98 | /// 99 | /// ```rust,ignore 100 | /// let v = f.as_filelike_view::(); 101 | /// (&*v).read(&mut buf).unwrap(); 102 | /// ``` 103 | /// 104 | /// [`File`]: std::fs::File 105 | /// [`Read`]: std::io::Read 106 | /// [`Write`]: std::io::Write 107 | fn as_filelike_view(&self) -> FilelikeView<'_, Target>; 108 | } 109 | 110 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 111 | impl AsFilelike for T { 112 | #[inline] 113 | fn as_filelike(&self) -> BorrowedFilelike<'_> { 114 | self.as_fd() 115 | } 116 | 117 | #[inline] 118 | fn as_filelike_view(&self) -> FilelikeView<'_, Target> { 119 | FilelikeView::new(self) 120 | } 121 | } 122 | 123 | /// A portable trait to borrow a reference from an underlying filelike object. 124 | /// 125 | /// This is a portability abstraction over Unix-like `AsFd` and Windows' 126 | /// [`AsHandle`]. It also provides the `as_filelike_view` convenience function 127 | /// providing typed views. 128 | #[cfg(windows)] 129 | pub trait AsFilelike: AsHandle { 130 | /// Borrows the reference. 131 | /// 132 | /// # Example 133 | /// 134 | /// ```rust,no_run 135 | /// use std::fs::File; 136 | /// # use std::io; 137 | /// use io_lifetimes::{AsFilelike, BorrowedFilelike}; 138 | /// 139 | /// let mut f = File::open("foo.txt")?; 140 | /// let borrowed_filelike: BorrowedFilelike<'_> = f.as_filelike(); 141 | /// # Ok::<(), io::Error>(()) 142 | /// ``` 143 | fn as_filelike(&self) -> BorrowedFilelike<'_>; 144 | 145 | /// Return a borrowing view of a resource which dereferences to a 146 | /// `&Target`. 147 | /// 148 | /// Note that [`Read`] or [`Write`] require `&mut Target`, but in some 149 | /// cases, such as [`File`], `Read` and `Write` are implemented for 150 | /// `&Target` in addition to `Target`, and you can get a `&mut &Target` 151 | /// by doing `&*` on the resuting view, like this: 152 | /// 153 | /// ```rust,ignore 154 | /// let v = f.as_filelike_view::(); 155 | /// (&*v).read(&mut buf).unwrap(); 156 | /// ``` 157 | /// 158 | /// [`File`]: std::fs::File 159 | /// [`Read`]: std::io::Read 160 | /// [`Write`]: std::io::Write 161 | fn as_filelike_view(&self) -> FilelikeView<'_, Target>; 162 | } 163 | 164 | #[cfg(windows)] 165 | impl AsFilelike for T { 166 | #[inline] 167 | fn as_filelike(&self) -> BorrowedFilelike<'_> { 168 | self.as_handle() 169 | } 170 | 171 | #[inline] 172 | fn as_filelike_view(&self) -> FilelikeView<'_, Target> { 173 | FilelikeView::new(self) 174 | } 175 | } 176 | 177 | /// A portable trait to borrow a reference from an underlying socketlike 178 | /// object. 179 | /// 180 | /// This is a portability abstraction over Unix-like [`AsFd`] and Windows' 181 | /// `AsSocket`. It also provides the `as_socketlike_view` convenience 182 | /// function providing typed views. 183 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 184 | pub trait AsSocketlike: AsFd { 185 | /// Borrows the reference. 186 | fn as_socketlike(&self) -> BorrowedSocketlike<'_>; 187 | 188 | /// Return a borrowing view of a resource which dereferences to a 189 | /// `&Target`. 190 | /// 191 | /// Note that [`Read`] or [`Write`] require `&mut Target`, but in some 192 | /// cases, such as [`TcpStream`], `Read` and `Write` are implemented 193 | /// for `&Target` in addition to `Target`, and you can get a `&mut 194 | /// &Target` by doing `&*` on the resuting view, like this: 195 | /// 196 | /// ```rust,ignore 197 | /// let v = s.as_socketlike_view::(); 198 | /// (&*v).read(&mut buf).unwrap(); 199 | /// ``` 200 | /// 201 | /// [`TcpStream`]: std::net::TcpStream 202 | /// [`Read`]: std::io::Read 203 | /// [`Write`]: std::io::Write 204 | fn as_socketlike_view(&self) -> SocketlikeView<'_, Target>; 205 | } 206 | 207 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 208 | impl AsSocketlike for T { 209 | #[inline] 210 | fn as_socketlike(&self) -> BorrowedSocketlike<'_> { 211 | self.as_fd() 212 | } 213 | 214 | #[inline] 215 | fn as_socketlike_view(&self) -> SocketlikeView<'_, Target> { 216 | SocketlikeView::new(self) 217 | } 218 | } 219 | 220 | /// A portable trait to borrow a reference from an underlying socketlike 221 | /// object. 222 | /// 223 | /// This is a portability abstraction over Unix-like `AsFd` and Windows' 224 | /// [`AsSocket`]. It also provides the `as_socketlike_view` convenience 225 | /// function providing typed views. 226 | #[cfg(windows)] 227 | pub trait AsSocketlike: AsSocket { 228 | /// Borrows the reference. 229 | fn as_socketlike(&self) -> BorrowedSocketlike<'_>; 230 | 231 | /// Return a borrowing view of a resource which dereferences to a 232 | /// `&Target`. 233 | /// 234 | /// Note that [`Read`] or [`Write`] require `&mut Target`, but in some 235 | /// cases, such as [`TcpStream`], `Read` and `Write` are implemented 236 | /// for `&Target` in addition to `Target`, and you can get a `&mut 237 | /// &Target` by doing `&*` on the resuting view, like this: 238 | /// 239 | /// ```rust,ignore 240 | /// let v = s.as_socketlike_view::(); 241 | /// (&*v).read(&mut buf).unwrap(); 242 | /// ``` 243 | /// 244 | /// [`TcpStream`]: std::net::TcpStream 245 | fn as_socketlike_view(&self) -> SocketlikeView<'_, Target>; 246 | } 247 | 248 | #[cfg(windows)] 249 | impl AsSocketlike for T { 250 | #[inline] 251 | fn as_socketlike(&self) -> BorrowedSocketlike<'_> { 252 | self.as_socket() 253 | } 254 | 255 | #[inline] 256 | fn as_socketlike_view(&self) -> SocketlikeView<'_, Target> { 257 | SocketlikeView::new(self) 258 | } 259 | } 260 | 261 | /// A portable trait to express the ability to consume an object and acquire 262 | /// ownership of its filelike object. 263 | /// 264 | /// This is a portability abstraction over Unix-like [`Into`] and 265 | /// Windows' `Into`. 266 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 267 | pub trait IntoFilelike: Into { 268 | /// Consumes this object, returning the underlying filelike object. 269 | /// 270 | /// # Example 271 | /// 272 | /// ```rust,no_run 273 | /// use std::fs::File; 274 | /// # use std::io; 275 | /// use io_lifetimes::{IntoFilelike, OwnedFilelike}; 276 | /// 277 | /// let f = File::open("foo.txt")?; 278 | /// let owned_filelike: OwnedFilelike = f.into_filelike(); 279 | /// # Ok::<(), io::Error>(()) 280 | /// ``` 281 | fn into_filelike(self) -> OwnedFilelike; 282 | } 283 | 284 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 285 | impl> IntoFilelike for T { 286 | #[inline] 287 | fn into_filelike(self) -> OwnedFilelike { 288 | self.into() 289 | } 290 | } 291 | 292 | /// A portable trait to express the ability to consume an object and acquire 293 | /// ownership of its filelike object. 294 | /// 295 | /// This is a portability abstraction over Unix-like `Into` and 296 | /// Windows' [`Into`]. 297 | #[cfg(windows)] 298 | pub trait IntoFilelike: Into { 299 | /// Consumes this object, returning the underlying filelike object. 300 | fn into_filelike(self) -> OwnedFilelike; 301 | } 302 | 303 | #[cfg(windows)] 304 | impl> IntoFilelike for T { 305 | #[inline] 306 | fn into_filelike(self) -> OwnedFilelike { 307 | self.into() 308 | } 309 | } 310 | 311 | /// A portable trait to express the ability to consume an object and acquire 312 | /// ownership of its socketlike object. 313 | /// 314 | /// This is a portability abstraction over Unix-like [`Into`] and 315 | /// Windows' `Into`. 316 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 317 | pub trait IntoSocketlike: Into { 318 | /// Consumes this object, returning the underlying socketlike object. 319 | fn into_socketlike(self) -> OwnedSocketlike; 320 | } 321 | 322 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 323 | impl> IntoSocketlike for T { 324 | #[inline] 325 | fn into_socketlike(self) -> OwnedSocketlike { 326 | self.into() 327 | } 328 | } 329 | 330 | /// A portable trait to express the ability to consume an object and acquire 331 | /// ownership of its socketlike object. 332 | /// 333 | /// This is a portability abstraction over Unix-like `Into` and 334 | /// Windows' [`Into`]. 335 | #[cfg(windows)] 336 | pub trait IntoSocketlike: Into { 337 | /// Consumes this object, returning the underlying socketlike object. 338 | /// 339 | /// # Example 340 | /// 341 | /// ```rust,no_run 342 | /// use std::fs::File; 343 | /// # use std::io; 344 | /// use io_lifetimes::{IntoFilelike, OwnedFilelike}; 345 | /// 346 | /// let f = File::open("foo.txt")?; 347 | /// let owned_filelike: OwnedFilelike = f.into_filelike(); 348 | /// # Ok::<(), io::Error>(()) 349 | /// ``` 350 | fn into_socketlike(self) -> OwnedSocketlike; 351 | } 352 | 353 | #[cfg(windows)] 354 | impl> IntoSocketlike for T { 355 | #[inline] 356 | fn into_socketlike(self) -> OwnedSocketlike { 357 | self.into() 358 | } 359 | } 360 | 361 | /// A portable trait to express the ability to construct an object from a 362 | /// filelike object. 363 | /// 364 | /// This is a portability abstraction over Unix-like [`From`] and 365 | /// Windows' `From`. It also provides the `from_into_filelike` 366 | /// convenience function providing simplified from+into conversions. 367 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 368 | pub trait FromFilelike: From { 369 | /// Constructs a new instance of `Self` from the given filelike object. 370 | /// 371 | /// # Example 372 | /// 373 | /// ```rust,no_run 374 | /// use std::fs::File; 375 | /// # use std::io; 376 | /// use io_lifetimes::{FromFilelike, IntoFilelike, OwnedFilelike}; 377 | /// 378 | /// let f = File::open("foo.txt")?; 379 | /// let owned_filelike: OwnedFilelike = f.into_filelike(); 380 | /// let f = File::from_filelike(owned_filelike); 381 | /// # Ok::<(), io::Error>(()) 382 | /// ``` 383 | fn from_filelike(owned: OwnedFilelike) -> Self; 384 | 385 | /// Constructs a new instance of `Self` from the given filelike object 386 | /// converted from `into_owned`. 387 | /// 388 | /// # Example 389 | /// 390 | /// ```rust,no_run 391 | /// use std::fs::File; 392 | /// # use std::io; 393 | /// use io_lifetimes::{FromFilelike, IntoFilelike}; 394 | /// 395 | /// let f = File::open("foo.txt")?; 396 | /// let f = File::from_into_filelike(f); 397 | /// # Ok::<(), io::Error>(()) 398 | /// ``` 399 | fn from_into_filelike(owned: Owned) -> Self; 400 | } 401 | 402 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 403 | impl> FromFilelike for T { 404 | #[inline] 405 | fn from_filelike(owned: OwnedFilelike) -> Self { 406 | Self::from(owned) 407 | } 408 | 409 | #[inline] 410 | fn from_into_filelike(owned: Owned) -> Self { 411 | Self::from_filelike(owned.into_filelike()) 412 | } 413 | } 414 | 415 | /// A portable trait to express the ability to construct an object from a 416 | /// filelike object. 417 | /// 418 | /// This is a portability abstraction over Unix-like `From` and 419 | /// Windows' [`From`]. It also provides the `from_into_filelike` 420 | /// convenience function providing simplified from+into conversions. 421 | #[cfg(windows)] 422 | pub trait FromFilelike: From { 423 | /// Constructs a new instance of `Self` from the given filelike object. 424 | /// 425 | /// # Example 426 | /// 427 | /// ```rust,no_run 428 | /// use std::fs::File; 429 | /// # use std::io; 430 | /// use io_lifetimes::{FromFilelike, IntoFilelike, OwnedFilelike}; 431 | /// 432 | /// let f = File::open("foo.txt")?; 433 | /// let owned_filelike: OwnedFilelike = f.into_filelike(); 434 | /// let f = File::from_filelike(owned_filelike); 435 | /// # Ok::<(), io::Error>(()) 436 | /// ``` 437 | fn from_filelike(owned: OwnedFilelike) -> Self; 438 | 439 | /// Constructs a new instance of `Self` from the given filelike object 440 | /// converted from `into_owned`. 441 | /// 442 | /// # Example 443 | /// 444 | /// ```rust,no_run 445 | /// use std::fs::File; 446 | /// # use std::io; 447 | /// use io_lifetimes::{FromFilelike, IntoFilelike}; 448 | /// 449 | /// let f = File::open("foo.txt")?; 450 | /// let f = File::from_into_filelike(f); 451 | /// # Ok::<(), io::Error>(()) 452 | /// ``` 453 | fn from_into_filelike(owned: Owned) -> Self; 454 | } 455 | 456 | #[cfg(windows)] 457 | impl> FromFilelike for T { 458 | #[inline] 459 | fn from_filelike(owned: OwnedFilelike) -> Self { 460 | Self::from(owned) 461 | } 462 | 463 | #[inline] 464 | fn from_into_filelike(owned: Owned) -> Self { 465 | Self::from_filelike(owned.into_filelike()) 466 | } 467 | } 468 | 469 | /// A portable trait to express the ability to construct an object from a 470 | /// socketlike object. 471 | /// 472 | /// This is a portability abstraction over Unix-like [`From`] and 473 | /// Windows' `From` It also provides the 474 | /// `from_into_socketlike` convenience function providing simplified from+into 475 | /// conversions. 476 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 477 | pub trait FromSocketlike: From { 478 | /// Constructs a new instance of `Self` from the given socketlike object. 479 | fn from_socketlike(owned: OwnedSocketlike) -> Self; 480 | 481 | /// Constructs a new instance of `Self` from the given socketlike object 482 | /// converted from `into_owned`. 483 | fn from_into_socketlike(owned: Owned) -> Self; 484 | } 485 | 486 | #[cfg(any(unix, target_os = "wasi", target_os = "hermit"))] 487 | impl> FromSocketlike for T { 488 | #[inline] 489 | fn from_socketlike(owned: OwnedSocketlike) -> Self { 490 | Self::from(owned) 491 | } 492 | 493 | #[inline] 494 | fn from_into_socketlike(owned: Owned) -> Self { 495 | Self::from_socketlike(owned.into_socketlike()) 496 | } 497 | } 498 | 499 | /// A portable trait to express the ability to construct an object from a 500 | /// socketlike object. 501 | /// 502 | /// This is a portability abstraction over Unix-like `From` and 503 | /// Windows' [`From`]. It also provides the `from_into_socketlike` 504 | /// convenience function providing simplified from+into conversions. 505 | #[cfg(windows)] 506 | pub trait FromSocketlike: From { 507 | /// Constructs a new instance of `Self` from the given socketlike object. 508 | fn from_socketlike(owned: OwnedSocketlike) -> Self; 509 | 510 | /// Constructs a new instance of `Self` from the given socketlike object 511 | /// converted from `into_owned`. 512 | fn from_into_socketlike(owned: Owned) -> Self; 513 | } 514 | 515 | #[cfg(windows)] 516 | impl> FromSocketlike for T { 517 | #[inline] 518 | fn from_socketlike(owned: OwnedSocketlike) -> Self { 519 | Self::from(owned) 520 | } 521 | 522 | #[inline] 523 | fn from_into_socketlike(owned: Owned) -> Self { 524 | Self::from_socketlike(owned.into_socketlike()) 525 | } 526 | } 527 | --------------------------------------------------------------------------------