coreutils_rs/common/
io.rs1use std::fs::{self, File};
2use std::io::{self, Read};
3use std::ops::Deref;
4use std::path::Path;
5
6#[cfg(target_os = "linux")]
7use std::sync::atomic::{AtomicBool, Ordering};
8
9use memmap2::{Mmap, MmapOptions};
10
11pub enum FileData {
14 Mmap(Mmap),
15 Owned(Vec<u8>),
16}
17
18impl Deref for FileData {
19 type Target = [u8];
20
21 fn deref(&self) -> &[u8] {
22 match self {
23 FileData::Mmap(m) => m,
24 FileData::Owned(v) => v,
25 }
26 }
27}
28
29const MMAP_THRESHOLD: u64 = 1024 * 1024;
34
35#[cfg(target_os = "linux")]
38static NOATIME_SUPPORTED: AtomicBool = AtomicBool::new(true);
39
40#[cfg(target_os = "linux")]
43fn open_noatime(path: &Path) -> io::Result<File> {
44 use std::os::unix::fs::OpenOptionsExt;
45 if NOATIME_SUPPORTED.load(Ordering::Relaxed) {
46 match fs::OpenOptions::new()
47 .read(true)
48 .custom_flags(libc::O_NOATIME)
49 .open(path)
50 {
51 Ok(f) => return Ok(f),
52 Err(ref e) if e.raw_os_error() == Some(libc::EPERM) => {
53 NOATIME_SUPPORTED.store(false, Ordering::Relaxed);
55 }
56 Err(e) => return Err(e), }
58 }
59 File::open(path)
60}
61
62#[cfg(not(target_os = "linux"))]
63fn open_noatime(path: &Path) -> io::Result<File> {
64 File::open(path)
65}
66
67pub fn read_file(path: &Path) -> io::Result<FileData> {
70 let file = open_noatime(path)?;
71 let metadata = file.metadata()?;
72 let len = metadata.len();
73
74 if len > 0 && metadata.file_type().is_file() {
75 if len < MMAP_THRESHOLD {
79 let mut buf = vec![0u8; len as usize];
80 let n = read_full(&mut &file, &mut buf)?;
81 buf.truncate(n);
82 return Ok(FileData::Owned(buf));
83 }
84
85 match unsafe { MmapOptions::new().populate().map(&file) } {
88 Ok(mmap) => {
89 #[cfg(target_os = "linux")]
90 {
91 let _ = mmap.advise(memmap2::Advice::Sequential);
92 if len >= 2 * 1024 * 1024 {
96 let _ = mmap.advise(memmap2::Advice::HugePage);
97 }
98 }
99 Ok(FileData::Mmap(mmap))
100 }
101 Err(_) => {
102 let mut buf = Vec::with_capacity(len as usize);
104 let mut reader = file;
105 reader.read_to_end(&mut buf)?;
106 Ok(FileData::Owned(buf))
107 }
108 }
109 } else if len > 0 {
110 let mut buf = Vec::new();
112 let mut reader = file;
113 reader.read_to_end(&mut buf)?;
114 Ok(FileData::Owned(buf))
115 } else {
116 Ok(FileData::Owned(Vec::new()))
117 }
118}
119
120pub fn file_size(path: &Path) -> io::Result<u64> {
122 Ok(fs::metadata(path)?.len())
123}
124
125pub fn read_stdin() -> io::Result<Vec<u8>> {
127 let mut buf = Vec::new();
128 io::stdin().lock().read_to_end(&mut buf)?;
129 Ok(buf)
130}
131
132#[inline]
136fn read_full(reader: &mut impl Read, buf: &mut [u8]) -> io::Result<usize> {
137 let mut total = 0;
138 while total < buf.len() {
139 match reader.read(&mut buf[total..]) {
140 Ok(0) => break,
141 Ok(n) => total += n,
142 Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
143 Err(e) => return Err(e),
144 }
145 }
146 Ok(total)
147}