mapped_file/
file.rs

1//! Types for, and operations on file descriptors. Useful for mapping
2use super::*;
3
4/// Raw file-descriptor for standard input
5pub const STDIN_FILENO: RawFd = libc::STDIN_FILENO;
6/// Raw file-descriptor for standard output
7pub const STDOUT_FILENO: RawFd = libc::STDOUT_FILENO;
8/// Raw file-descriptor for standard error
9pub const STDERR_FILENO: RawFd = libc::STDERR_FILENO;
10
11mod raw;
12use raw::*;
13
14mod managed;
15mod unmanaged;
16
17pub use self::{
18    managed::*,
19    unmanaged::*,
20};
21
22pub mod memory;
23
24#[derive(Debug)]
25enum MaybeMappedInner<T>
26{
27    Raw(T),
28    Copied(memory::MemoryFile),
29}
30
31impl<T: AsRawFd + io::Read> MaybeMappedInner<T>
32{
33    pub fn from_stat(mut file: T) -> io::Result<(Self, u64)>
34    {
35	use libc::fstat;
36	let fd = file.as_raw_fd();
37	let sz = unsafe {
38	    let mut stat = std::mem::MaybeUninit::uninit();
39	    if fstat(fd, stat.as_mut_ptr()) != 0 {
40		let mut mem = memory::MemoryFile::new()?;
41		let count = std::io::copy(&mut file, &mut mem)?;
42		return Ok((Self::Copied(mem), count));
43	    }
44	    stat.assume_init().st_size & i64::MAX
45	} as u64;
46	Ok((Self::Raw(file), sz))
47    }
48}
49
50impl<T: IntoRawFd> MaybeMappedInner<T>
51{
52    #[inline] 
53    pub unsafe fn into_file(self) -> std::fs::File
54    {
55	let fd = match self {
56	    Self::Raw(r) => r.into_raw_fd(),
57	    Self::Copied(c) => c.into_raw_fd(),
58	};
59	
60	FromRawFd::from_raw_fd(fd)
61    }
62}
63
64impl<T> AsRawFd for MaybeMappedInner<T>
65where T: AsRawFd
66{
67    #[inline] 
68    fn as_raw_fd(&self) -> RawFd {
69	match self {
70	    Self::Copied(c) => c.as_raw_fd(),
71	    Self::Raw(r) => r.as_raw_fd(),
72	}
73    }
74}
75
76/// Attempt to map a file, if it fails, copy that file into memory and map that.
77///
78/// # Returns
79/// A map over the file, or a map over an in-memory copy of the file.
80pub fn try_map_or_cloned<F: io::Read + AsRawFd + IntoRawFd>(file: F, perm: Perm, flags: impl MapFlags) -> io::Result<MappedFile<std::fs::File>>
81{
82    let (len, file) = {
83	let (file, size) = MaybeMappedInner::from_stat(file)?;
84	let size = usize::try_from(size).map_err(|_| io::Error::new(io::ErrorKind::Unsupported, "File size exceeds pointer word width"))?;
85	(size, unsafe {
86	    file.into_file() 
87	})
88    };
89    MappedFile::new(file, len, perm, flags)
90}
91
92#[cfg(test)]
93mod tests
94{
95    use super::*;
96    
97    #[test]
98    fn std_in_out_err_fileno()
99    {
100	#[inline(always)]
101	fn test_fileno<const EXPECTED: RawFd>(expected_name: &'static str, got: RawFd)
102	{
103	    assert_eq!(EXPECTED, got, "{expected_name} invalid: expected: {EXPECTED}, got {got}");
104	}
105
106	test_fileno::<STDIN_FILENO>("STDIN_FILENO", std::io::stdin().as_raw_fd());
107	test_fileno::<STDOUT_FILENO>("STDOUT_FILENO", std::io::stdout().as_raw_fd());
108	test_fileno::<STDERR_FILENO>("STDERR_FILENO", std::io::stderr().as_raw_fd());
109    }
110
111    #[test]
112    fn test_readwrite()
113    {
114	let mut input = ManagedFD::from(memory::MemoryFile::new().unwrap());
115	let mut output = memory::MemoryFile::new().unwrap();
116	assert_eq!(std::io::copy(&mut input, &mut output).unwrap(), 0, "Bad read");
117
118    }
119}