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.as_slice().len()
90 }
91
92 fn is_set(&self, index: usize) -> bool {
93 self.as_slice().is_set(index)
94 }
95
96 fn count_filled(&self) -> usize {
97 self.as_slice().count_filled()
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.as_bitslice().len()
141 }
142
143 fn is_set(&self, index: usize) -> bool {
144 self.as_bitslice().is_set(index)
145 }
146
147 fn count_filled(&self) -> usize {
148 self.as_bitslice().count_filled()
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 word_bytes = std::mem::size_of::<usize>();
166 let bits_per_word = usize::BITS as usize;
167
168 let packed: Vec<usize> = bytes
169 .chunks(bits_per_word)
170 .map(|chunk| {
171 let mut buf = [0u8; std::mem::size_of::<usize>()];
172 for (byte_pos, sub) in chunk.chunks(8).enumerate().take(word_bytes) {
173 let mut packed_byte = 0u8;
174 for (bit, &b) in sub.iter().enumerate() {
175 packed_byte |= ((b != 0) as u8) << bit;
176 }
177 buf[byte_pos] = packed_byte;
178 }
179 usize::from_le_bytes(buf)
180 })
181 .collect();
182 let mut bv = BitVec::from_vec(packed);
183 bv.truncate(len);
184 bv
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use memmap2::MmapOptions;
193 use std::io::Write;
194
195 fn create_temp_file(size: usize) -> (tempfile::NamedTempFile, Mmap) {
196 let mut f = tempfile::NamedTempFile::new().unwrap();
197 let data = vec![0xABu8; size];
198 f.write_all(&data).unwrap();
199 f.flush().unwrap();
200
201 let mmap = unsafe { MmapOptions::new().map(f.as_file()).unwrap() };
202 (f, mmap)
203 }
204
205 macro_rules! residency_tests {
206 ($t:ty, $mod:ident) => {
207 mod $mod {
208 use super::*;
209
210 #[test]
211 fn page_count() {
212 let ps = *crate::pagesize::PAGE_SIZE;
213 let size = ps * 4;
214 let (_f, mmap) = create_temp_file(size);
215 let res: $t = residency(&mmap, size).unwrap();
216 assert_eq!(res.len(), 4);
217 }
218
219 #[test]
220 fn after_touch_all_resident() {
221 let ps = *crate::pagesize::PAGE_SIZE;
222 let size = ps * 4;
223 let (_f, mmap) = create_temp_file(size);
224
225 let mut junk: u8 = 0;
226 for i in 0..4 {
227 junk = junk.wrapping_add(mmap[i * ps]);
228 }
229 let _ = junk;
230
231 let res: $t = residency(&mmap, size).unwrap();
232 assert!(res.iter().all(|r| *r));
233 }
234
235 #[test]
236 fn partial_page_rounds_up() {
237 let ps = *crate::pagesize::PAGE_SIZE;
238 let size = ps * 3 + 1;
239 let (_f, mmap) = create_temp_file(size);
240 let res: $t = residency(&mmap, size).unwrap();
241 assert_eq!(res.len(), 4);
242 }
243
244 #[test]
245 fn single_page() {
246 let ps = *crate::pagesize::PAGE_SIZE;
247 let (_f, mmap) = create_temp_file(ps);
248 let _ = mmap[0];
249 let res: $t = residency(&mmap, ps).unwrap();
250 assert_eq!(res.len(), 1);
251 assert_eq!(PageMapSlice::count_filled(&res), 1);
252 }
253
254 #[test]
255 fn count_filled_after_touch() {
256 let ps = *crate::pagesize::PAGE_SIZE;
257 let size = ps * 4;
258 let (_f, mmap) = create_temp_file(size);
259 let _ = mmap[0];
260 let _ = mmap[ps * 2];
261 let res: $t = residency(&mmap, size).unwrap();
262 assert!(PageMapSlice::count_filled(&res) >= 2);
263 }
264
265 #[test]
266 fn slice_range() {
267 let ps = *crate::pagesize::PAGE_SIZE;
268 let size = ps * 4;
269 let (_f, mmap) = create_temp_file(size);
270 let res: $t = residency(&mmap, size).unwrap();
271 let slice = &res[1..3];
272 assert_eq!(PageMapSlice::len(slice), 2);
273 }
274
275 #[test]
276 fn fill_sets_all() {
277 let mut pm = <$t>::from_bools([true, false, true, false].into_iter());
278 PageMapSlice::fill(&mut pm, true);
279 assert_eq!(PageMapSlice::count_filled(&pm), 4);
280 PageMapSlice::fill(&mut pm, false);
281 assert_eq!(PageMapSlice::count_filled(&pm), 0);
282 }
283
284 #[test]
285 fn from_bools_empty() {
286 let pm = <$t>::from_bools(std::iter::empty());
287 assert!(PageMapSlice::is_empty(&pm));
288 }
289
290 #[test]
291 fn from_residency_bytes_empty() {
292 let pm = <$t>::from_residency_bytes(vec![]);
293 assert!(PageMapSlice::is_empty(&pm));
294 }
295
296 #[test]
297 fn from_residency_bytes_all_zero() {
298 let pm = <$t>::from_residency_bytes(vec![0; 8]);
299 assert_eq!(PageMapSlice::len(&pm), 8);
300 assert_eq!(PageMapSlice::count_filled(&pm), 0);
301 }
302
303 #[test]
304 fn from_residency_bytes_all_resident() {
305 let pm = <$t>::from_residency_bytes(vec![1; 8]);
306 assert_eq!(PageMapSlice::len(&pm), 8);
307 assert_eq!(PageMapSlice::count_filled(&pm), 8);
308 }
309
310 #[test]
311 fn from_residency_bytes_nonzero_values() {
312 let pm = <$t>::from_residency_bytes(vec![0xFF, 0x02, 0x80, 0]);
313 assert_eq!(PageMapSlice::len(&pm), 4);
314 assert_eq!(PageMapSlice::count_filled(&pm), 3);
315 }
316
317 #[test]
318 fn from_residency_bytes_single() {
319 let resident = <$t>::from_residency_bytes(vec![1]);
320 assert_eq!(PageMapSlice::len(&resident), 1);
321 assert_eq!(PageMapSlice::count_filled(&resident), 1);
322
323 let absent = <$t>::from_residency_bytes(vec![0]);
324 assert_eq!(PageMapSlice::len(&absent), 1);
325 assert_eq!(PageMapSlice::count_filled(&absent), 0);
326 }
327
328 #[test]
329 fn from_residency_bytes_alternating() {
330 let pm = <$t>::from_residency_bytes(vec![1, 0, 1, 0, 1, 0]);
331 assert_eq!(PageMapSlice::len(&pm), 6);
332 assert_eq!(PageMapSlice::count_filled(&pm), 3);
333 }
334
335 #[test]
336 fn from_residency_bytes_matches_from_bools() {
337 let bytes = vec![0u8, 1, 0, 0xFF, 1, 0, 0x42, 0];
338 let from_bytes = <$t>::from_residency_bytes(bytes.clone());
339 let from_bools = <$t>::from_bools(bytes.iter().map(|&b| b != 0));
340 assert_eq!(
341 PageMapSlice::len(&from_bytes),
342 PageMapSlice::len(&from_bools)
343 );
344 assert_eq!(
345 PageMapSlice::count_filled(&from_bytes),
346 PageMapSlice::count_filled(&from_bools),
347 );
348 }
349
350 #[test]
351 fn from_residency_bytes_across_word_boundary() {
352 let mut bytes = vec![1u8; 65];
354 bytes[63] = 0;
355 bytes[64] = 1;
356 let pm = <$t>::from_residency_bytes(bytes);
357 assert_eq!(PageMapSlice::len(&pm), 65);
358 assert_eq!(PageMapSlice::count_filled(&pm), 64);
359 }
360 }
361 };
362 }
363
364 residency_tests!(Vec<bool>, vec_bool_impl);
365
366 #[cfg(feature = "bitvec")]
367 residency_tests!(::bitvec::vec::BitVec, bitvec_impl);
368}