1use std::ops::{Index, IndexMut, Range};
2
3use memmap2::Mmap;
4use nix::errno::Errno;
5
6#[cfg(target_os = "macos")]
7type MincoreType = libc::c_char;
8
9#[cfg(not(target_os = "macos"))]
10type MincoreType = libc::c_uchar;
11
12pub fn residency<PM: PageMap>(mmap: &Mmap, len: usize) -> nix::Result<PM> {
13 let page_size = *crate::pagesize::PAGE_SIZE;
14 let vec_len = len.div_ceil(page_size);
15 let mut vec_out: Vec<u8> = Vec::with_capacity(vec_len);
16
17 unsafe {
18 if libc::mincore(
23 mmap.as_ptr() as *mut libc::c_void,
24 len as libc::size_t,
25 vec_out.as_mut_ptr().cast::<MincoreType>(),
26 ) != 0
27 {
28 return Err(Errno::last());
29 }
30 vec_out.set_len(vec_len);
32 }
33 Ok(PM::from_residency_bytes(vec_out))
34}
35
36pub trait PageMapSlice {
37 fn len(&self) -> usize;
38 fn is_empty(&self) -> bool {
39 self.len() == 0
40 }
41 fn is_set(&self, index: usize) -> bool;
42 fn count_filled(&self) -> usize;
43 fn fill(&mut self, value: bool);
44}
45
46pub trait PageMap:
47 PageMapSlice + Index<Range<usize>, Output = Self::Slice> + IndexMut<Range<usize>>
48{
49 type Slice: ?Sized + PageMapSlice;
50
51 fn from_bools(iter: impl Iterator<Item = bool>) -> Self;
52
53 fn from_residency_bytes(bytes: Vec<u8>) -> Self
54 where
55 Self: Sized,
56 {
57 Self::from_bools(bytes.into_iter().map(|b| b != 0))
58 }
59}
60
61#[cfg(feature = "bitvec")]
62pub type DefaultPageMap = bitvec::vec::BitVec;
63
64#[cfg(not(feature = "bitvec"))]
65pub type DefaultPageMap = Vec<bool>;
66
67impl PageMapSlice for [bool] {
70 fn len(&self) -> usize {
71 self.len()
72 }
73
74 fn is_set(&self, index: usize) -> bool {
75 self[index]
76 }
77
78 fn count_filled(&self) -> usize {
79 self.iter().filter(|&&v| v).count()
80 }
81
82 fn fill(&mut self, value: bool) {
83 self.fill(value);
84 }
85}
86
87impl PageMapSlice for Vec<bool> {
88 fn len(&self) -> usize {
89 self.len()
90 }
91
92 fn is_set(&self, index: usize) -> bool {
93 self[index]
94 }
95
96 fn count_filled(&self) -> usize {
97 self.iter().filter(|&&v| v).count()
98 }
99
100 fn fill(&mut self, value: bool) {
101 self.as_mut_slice().fill(value);
102 }
103}
104
105impl PageMap for Vec<bool> {
106 type Slice = [bool];
107
108 fn from_bools(iter: impl Iterator<Item = bool>) -> Self {
109 iter.collect()
110 }
111}
112
113#[cfg(feature = "bitvec")]
116mod bitvec_impl {
117 use super::*;
118 use bitvec::prelude::*;
119
120 impl PageMapSlice for BitSlice {
121 fn len(&self) -> usize {
122 self.len()
123 }
124
125 fn is_set(&self, index: usize) -> bool {
126 self[index]
127 }
128
129 fn count_filled(&self) -> usize {
130 self.count_ones()
131 }
132
133 fn fill(&mut self, value: bool) {
134 self.fill(value);
135 }
136 }
137
138 impl PageMapSlice for BitVec {
139 fn len(&self) -> usize {
140 self.len()
141 }
142
143 fn is_set(&self, index: usize) -> bool {
144 self[index]
145 }
146
147 fn count_filled(&self) -> usize {
148 self.count_ones()
149 }
150
151 fn fill(&mut self, value: bool) {
152 self.as_mut_bitslice().fill(value);
153 }
154 }
155
156 impl PageMap for BitVec {
157 type Slice = BitSlice;
158
159 fn from_bools(iter: impl Iterator<Item = bool>) -> Self {
160 iter.collect()
161 }
162
163 fn from_residency_bytes(bytes: Vec<u8>) -> Self {
164 let len = bytes.len();
165 let bits_per_word = usize::BITS as usize;
166 let packed: Vec<usize> = bytes
167 .chunks(bits_per_word)
168 .map(|chunk| {
169 chunk
170 .iter()
171 .enumerate()
172 .fold(0usize, |acc, (i, &b)| acc | (((b != 0) as usize) << i))
173 })
174 .collect();
175 let mut bv = BitVec::from_vec(packed);
176 bv.truncate(len);
177 bv
178 }
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use memmap2::MmapOptions;
186 use std::io::Write;
187
188 fn create_temp_file(size: usize) -> (tempfile::NamedTempFile, Mmap) {
189 let mut f = tempfile::NamedTempFile::new().unwrap();
190 let data = vec![0xABu8; size];
191 f.write_all(&data).unwrap();
192 f.flush().unwrap();
193
194 let mmap = unsafe { MmapOptions::new().map(f.as_file()).unwrap() };
195 (f, mmap)
196 }
197
198 macro_rules! residency_tests {
199 ($t:ty, $mod:ident) => {
200 mod $mod {
201 use super::*;
202
203 #[test]
204 fn page_count() {
205 let ps = *crate::pagesize::PAGE_SIZE;
206 let size = ps * 4;
207 let (_f, mmap) = create_temp_file(size);
208 let res: $t = residency(&mmap, size).unwrap();
209 assert_eq!(res.len(), 4);
210 }
211
212 #[test]
213 fn after_touch_all_resident() {
214 let ps = *crate::pagesize::PAGE_SIZE;
215 let size = ps * 4;
216 let (_f, mmap) = create_temp_file(size);
217
218 let mut junk: u8 = 0;
219 for i in 0..4 {
220 junk = junk.wrapping_add(mmap[i * ps]);
221 }
222 let _ = junk;
223
224 let res: $t = residency(&mmap, size).unwrap();
225 assert!(res.iter().all(|r| *r));
226 }
227
228 #[test]
229 fn partial_page_rounds_up() {
230 let ps = *crate::pagesize::PAGE_SIZE;
231 let size = ps * 3 + 1;
232 let (_f, mmap) = create_temp_file(size);
233 let res: $t = residency(&mmap, size).unwrap();
234 assert_eq!(res.len(), 4);
235 }
236
237 #[test]
238 fn single_page() {
239 let ps = *crate::pagesize::PAGE_SIZE;
240 let (_f, mmap) = create_temp_file(ps);
241 let _ = mmap[0];
242 let res: $t = residency(&mmap, ps).unwrap();
243 assert_eq!(res.len(), 1);
244 assert_eq!(PageMapSlice::count_filled(&res), 1);
245 }
246
247 #[test]
248 fn count_filled_after_touch() {
249 let ps = *crate::pagesize::PAGE_SIZE;
250 let size = ps * 4;
251 let (_f, mmap) = create_temp_file(size);
252 let _ = mmap[0];
253 let _ = mmap[ps * 2];
254 let res: $t = residency(&mmap, size).unwrap();
255 assert!(PageMapSlice::count_filled(&res) >= 2);
256 }
257
258 #[test]
259 fn slice_range() {
260 let ps = *crate::pagesize::PAGE_SIZE;
261 let size = ps * 4;
262 let (_f, mmap) = create_temp_file(size);
263 let res: $t = residency(&mmap, size).unwrap();
264 let slice = &res[1..3];
265 assert_eq!(PageMapSlice::len(slice), 2);
266 }
267
268 #[test]
269 fn fill_sets_all() {
270 let mut pm = <$t>::from_bools([true, false, true, false].into_iter());
271 PageMapSlice::fill(&mut pm, true);
272 assert_eq!(PageMapSlice::count_filled(&pm), 4);
273 PageMapSlice::fill(&mut pm, false);
274 assert_eq!(PageMapSlice::count_filled(&pm), 0);
275 }
276
277 #[test]
278 fn from_bools_empty() {
279 let pm = <$t>::from_bools(std::iter::empty());
280 assert!(PageMapSlice::is_empty(&pm));
281 }
282
283 #[test]
284 fn from_residency_bytes_empty() {
285 let pm = <$t>::from_residency_bytes(vec![]);
286 assert!(PageMapSlice::is_empty(&pm));
287 }
288
289 #[test]
290 fn from_residency_bytes_all_zero() {
291 let pm = <$t>::from_residency_bytes(vec![0; 8]);
292 assert_eq!(PageMapSlice::len(&pm), 8);
293 assert_eq!(PageMapSlice::count_filled(&pm), 0);
294 }
295
296 #[test]
297 fn from_residency_bytes_all_resident() {
298 let pm = <$t>::from_residency_bytes(vec![1; 8]);
299 assert_eq!(PageMapSlice::len(&pm), 8);
300 assert_eq!(PageMapSlice::count_filled(&pm), 8);
301 }
302
303 #[test]
304 fn from_residency_bytes_nonzero_values() {
305 let pm = <$t>::from_residency_bytes(vec![0xFF, 0x02, 0x80, 0]);
306 assert_eq!(PageMapSlice::len(&pm), 4);
307 assert_eq!(PageMapSlice::count_filled(&pm), 3);
308 }
309
310 #[test]
311 fn from_residency_bytes_single() {
312 let resident = <$t>::from_residency_bytes(vec![1]);
313 assert_eq!(PageMapSlice::len(&resident), 1);
314 assert_eq!(PageMapSlice::count_filled(&resident), 1);
315
316 let absent = <$t>::from_residency_bytes(vec![0]);
317 assert_eq!(PageMapSlice::len(&absent), 1);
318 assert_eq!(PageMapSlice::count_filled(&absent), 0);
319 }
320
321 #[test]
322 fn from_residency_bytes_alternating() {
323 let pm = <$t>::from_residency_bytes(vec![1, 0, 1, 0, 1, 0]);
324 assert_eq!(PageMapSlice::len(&pm), 6);
325 assert_eq!(PageMapSlice::count_filled(&pm), 3);
326 }
327
328 #[test]
329 fn from_residency_bytes_matches_from_bools() {
330 let bytes = vec![0u8, 1, 0, 0xFF, 1, 0, 0x42, 0];
331 let from_bytes = <$t>::from_residency_bytes(bytes.clone());
332 let from_bools = <$t>::from_bools(bytes.iter().map(|&b| b != 0));
333 assert_eq!(
334 PageMapSlice::len(&from_bytes),
335 PageMapSlice::len(&from_bools)
336 );
337 assert_eq!(
338 PageMapSlice::count_filled(&from_bytes),
339 PageMapSlice::count_filled(&from_bools),
340 );
341 }
342
343 #[test]
344 fn from_residency_bytes_across_word_boundary() {
345 let mut bytes = vec![1u8; 65];
347 bytes[63] = 0;
348 bytes[64] = 1;
349 let pm = <$t>::from_residency_bytes(bytes);
350 assert_eq!(PageMapSlice::len(&pm), 65);
351 assert_eq!(PageMapSlice::count_filled(&pm), 64);
352 }
353 }
354 };
355 }
356
357 residency_tests!(Vec<bool>, vec_bool_impl);
358
359 #[cfg(feature = "bitvec")]
360 residency_tests!(::bitvec::vec::BitVec, bitvec_impl);
361}