Skip to content

Commit 30c6926

Browse files
committed
Fall back to reading instead of mmap on windows
Windows doesn't have support for mmap (at least not with the same apis)
1 parent 18b8c5a commit 30c6926

File tree

1 file changed

+109
-65
lines changed

1 file changed

+109
-65
lines changed

src/file_pool.rs

+109-65
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,127 @@
11
use anyhow::bail;
22
use core::slice;
3-
use libc::{
4-
c_void, mmap, munmap, sysconf, MAP_ANONYMOUS, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_READ,
5-
PROT_WRITE, _SC_PAGESIZE,
6-
};
7-
use std::{
8-
os::fd::{AsFd, AsRawFd},
9-
path::Path,
10-
ptr::null_mut,
11-
sync::Mutex,
12-
};
3+
use std::{path::Path, sync::Mutex};
134

14-
/// FilePool is a datastructure that is intended to hold onto byte buffers and give out immutable
15-
/// references to them. But it can also accept new byte buffers while old ones are still lent out.
16-
/// This requires interior mutability / unsafe code. Appending to a Vec while references to other
17-
/// elements are held is generally unsafe, because the Vec can reallocate all the prior elements
18-
/// to a new memory location. But if the elements themselves are pointers to stable memory, the
19-
/// contents of those pointers can be referenced safely. This also requires guarding the outer
20-
/// Vec with a Mutex so that two threads don't append to it at the same time.
21-
pub struct FilePool {
22-
files: Mutex<Vec<(*mut c_void, usize)>>,
23-
}
24-
impl FilePool {
25-
pub fn new() -> FilePool {
26-
FilePool {
27-
files: Mutex::new(Vec::new()),
28-
}
5+
#[cfg(unix)]
6+
mod mmap {
7+
use super::*;
8+
use libc::{
9+
c_void, mmap, munmap, sysconf, MAP_ANONYMOUS, MAP_FAILED, MAP_FIXED, MAP_PRIVATE,
10+
PROT_READ, PROT_WRITE, _SC_PAGESIZE,
11+
};
12+
use std::{
13+
os::fd::{AsFd, AsRawFd},
14+
ptr::null_mut,
15+
};
16+
/// FilePool is a datastructure that is intended to hold onto byte buffers and give out immutable
17+
/// references to them. But it can also accept new byte buffers while old ones are still lent out.
18+
/// This requires interior mutability / unsafe code. Appending to a Vec while references to other
19+
/// elements are held is generally unsafe, because the Vec can reallocate all the prior elements
20+
/// to a new memory location. But if the elements themselves are pointers to stable memory, the
21+
/// contents of those pointers can be referenced safely. This also requires guarding the outer
22+
/// Vec with a Mutex so that two threads don't append to it at the same time.
23+
pub struct FilePool {
24+
files: Mutex<Vec<(*mut c_void, usize)>>,
2925
}
30-
31-
pub fn read_file(&self, path: &Path) -> anyhow::Result<&[u8]> {
32-
let page_size = unsafe { sysconf(_SC_PAGESIZE) } as usize;
33-
let file = std::fs::File::open(path)?;
34-
let fd = file.as_fd().as_raw_fd();
35-
let file_size = file.metadata()?.len() as usize;
36-
let mapping_size = (file_size + page_size).next_multiple_of(page_size);
37-
unsafe {
38-
// size + 1 to add a null terminator.
39-
let addr = mmap(null_mut(), mapping_size, PROT_READ, MAP_PRIVATE, fd, 0);
40-
if addr == MAP_FAILED {
41-
bail!("mmap failed");
26+
impl FilePool {
27+
pub fn new() -> FilePool {
28+
FilePool {
29+
files: Mutex::new(Vec::new()),
4230
}
31+
}
32+
33+
pub fn read_file(&self, path: &Path) -> anyhow::Result<&[u8]> {
34+
let page_size = unsafe { sysconf(_SC_PAGESIZE) } as usize;
35+
let file = std::fs::File::open(path)?;
36+
let fd = file.as_fd().as_raw_fd();
37+
let file_size = file.metadata()?.len() as usize;
38+
let mapping_size = (file_size + page_size).next_multiple_of(page_size);
39+
unsafe {
40+
// size + 1 to add a null terminator.
41+
let addr = mmap(null_mut(), mapping_size, PROT_READ, MAP_PRIVATE, fd, 0);
42+
if addr == MAP_FAILED {
43+
bail!("mmap failed");
44+
}
45+
46+
let addr2 = mmap(
47+
addr.add(mapping_size).sub(page_size),
48+
page_size,
49+
PROT_READ | PROT_WRITE,
50+
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
51+
-1,
52+
0,
53+
);
54+
if addr2 == MAP_FAILED {
55+
bail!("mmap failed");
56+
}
57+
*(addr.add(mapping_size).sub(page_size) as *mut u8) = 0;
58+
// The manpages say the extra bytes past the end of the file are
59+
// zero-filled, but just to make sure:
60+
assert!(*(addr.add(file_size) as *mut u8) == 0);
61+
62+
let files = &mut self.files.lock().unwrap();
63+
files.push((addr, mapping_size));
4364

44-
let addr2 = mmap(
45-
addr.add(mapping_size).sub(page_size),
46-
page_size,
47-
PROT_READ | PROT_WRITE,
48-
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
49-
-1,
50-
0,
51-
);
52-
if addr2 == MAP_FAILED {
53-
bail!("mmap failed");
65+
Ok(slice::from_raw_parts(addr as *mut u8, file_size + 1))
5466
}
55-
*(addr.add(mapping_size).sub(page_size) as *mut u8) = 0;
56-
// The manpages say the extra bytes past the end of the file are
57-
// zero-filled, but just to make sure:
58-
assert!(*(addr.add(file_size) as *mut u8) == 0);
67+
}
68+
}
5969

60-
let files = &mut self.files.lock().unwrap();
61-
files.push((addr, mapping_size));
70+
// SAFETY: Sync isn't implemented automatically because we have a *mut pointer,
71+
// but that pointer isn't used at all aside from the drop implementation, so
72+
// we won't have data races.
73+
unsafe impl Sync for FilePool {}
74+
unsafe impl Send for FilePool {}
6275

63-
Ok(slice::from_raw_parts(addr as *mut u8, file_size + 1))
76+
impl Drop for FilePool {
77+
fn drop(&mut self) {
78+
let files = self.files.lock().unwrap();
79+
for &(addr, len) in files.iter() {
80+
unsafe {
81+
munmap(addr, len);
82+
}
83+
}
6484
}
6585
}
6686
}
6787

68-
// SAFETY: Sync isn't implemented automatically because we have a *mut pointer,
69-
// but that pointer isn't used at all aside from the drop implementation, so
70-
// we won't have data races.
71-
unsafe impl Sync for FilePool {}
72-
unsafe impl Send for FilePool {}
88+
#[cfg(not(unix))]
89+
mod read {
90+
use crate::scanner::read_file_with_nul;
7391

74-
impl Drop for FilePool {
75-
fn drop(&mut self) {
76-
let files = self.files.lock().unwrap();
77-
for &(addr, len) in files.iter() {
78-
unsafe {
79-
munmap(addr, len);
92+
use super::*;
93+
94+
/// FilePool is a datastructure that is intended to hold onto byte buffers and give out immutable
95+
/// references to them. But it can also accept new byte buffers while old ones are still lent out.
96+
/// This requires interior mutability / unsafe code. Appending to a Vec while references to other
97+
/// elements are held is generally unsafe, because the Vec can reallocate all the prior elements
98+
/// to a new memory location. But if the elements themselves are unchanging Vecs, the
99+
/// contents of those Vecs can be referenced safely. This also requires guarding the outer
100+
/// Vec with a Mutex so that two threads don't append to it at the same time.
101+
pub struct FilePool {
102+
files: Mutex<Vec<Vec<u8>>>,
103+
}
104+
105+
impl FilePool {
106+
pub fn new() -> FilePool {
107+
FilePool {
108+
files: Mutex::new(Vec::new()),
80109
}
81110
}
111+
112+
pub fn read_file(&self, path: &Path) -> anyhow::Result<&[u8]> {
113+
let bytes = read_file_with_nul(path)?;
114+
let addr = bytes.as_ptr();
115+
let len = bytes.len();
116+
self.files.lock().unwrap().push(bytes);
117+
118+
unsafe { Ok(slice::from_raw_parts(addr as *mut u8, len)) }
119+
}
82120
}
83121
}
122+
123+
#[cfg(unix)]
124+
pub use mmap::FilePool;
125+
126+
#[cfg(not(unix))]
127+
pub use read::FilePool;

0 commit comments

Comments
 (0)