wasmtime_runtime/mmap_vec.rs
1use crate::Mmap;
2use anyhow::{Context, Result};
3use std::fs::File;
4use std::ops::{Deref, DerefMut, Range};
5use std::path::Path;
6use std::sync::Arc;
7
8/// A type akin to `Vec<u8>`, but backed by `mmap` and able to be split.
9///
10/// This type is a non-growable owned list of bytes. It can be segmented into
11/// disjoint separately owned views akin to the `split_at` method on slices in
12/// Rust. An `MmapVec` is backed by an OS-level memory allocation and is not
13/// suitable for lots of small allocation (since it works at the page
14/// granularity).
15///
16/// An `MmapVec` is an owned value which means that owners have the ability to
17/// get exclusive access to the underlying bytes, enabling mutation.
18pub struct MmapVec {
19 mmap: Arc<Mmap>,
20 range: Range<usize>,
21}
22
23impl MmapVec {
24 /// Consumes an existing `mmap` and wraps it up into an `MmapVec`.
25 ///
26 /// The returned `MmapVec` will have the `size` specified, which can be
27 /// smaller than the region mapped by the `Mmap`. The returned `MmapVec`
28 /// will only have at most `size` bytes accessible.
29 pub fn new(mmap: Mmap, size: usize) -> MmapVec {
30 assert!(size <= mmap.len());
31 MmapVec {
32 mmap: Arc::new(mmap),
33 range: 0..size,
34 }
35 }
36
37 /// Creates a new zero-initialized `MmapVec` with the given `size`.
38 ///
39 /// This commit will return a new `MmapVec` suitably sized to hold `size`
40 /// bytes. All bytes will be initialized to zero since this is a fresh OS
41 /// page allocation.
42 pub fn with_capacity(size: usize) -> Result<MmapVec> {
43 Ok(MmapVec::new(Mmap::with_at_least(size)?, size))
44 }
45
46 /// Creates a new `MmapVec` from the contents of an existing `slice`.
47 ///
48 /// A new `MmapVec` is allocated to hold the contents of `slice` and then
49 /// `slice` is copied into the new mmap. It's recommended to avoid this
50 /// method if possible to avoid the need to copy data around.
51 pub fn from_slice(slice: &[u8]) -> Result<MmapVec> {
52 let mut result = MmapVec::with_capacity(slice.len())?;
53 result.copy_from_slice(slice);
54 Ok(result)
55 }
56
57 /// Creates a new `MmapVec` which is the `path` specified mmap'd into
58 /// memory.
59 ///
60 /// This function will attempt to open the file located at `path` and will
61 /// then use that file to learn about its size and map the full contents
62 /// into memory. This will return an error if the file doesn't exist or if
63 /// it's too large to be fully mapped into memory.
64 pub fn from_file(path: &Path) -> Result<MmapVec> {
65 let mmap = Mmap::from_file(path)
66 .with_context(|| format!("failed to create mmap for file: {}", path.display()))?;
67 let len = mmap.len();
68 Ok(MmapVec::new(mmap, len))
69 }
70
71 /// Splits the collection into two at the given index.
72 ///
73 /// Returns a separate `MmapVec` which shares the underlying mapping, but
74 /// only has access to elements in the range `[at, len)`. After the call,
75 /// the original `MmapVec` will be left with access to the elements in the
76 /// range `[0, at)`.
77 ///
78 /// This is an `O(1)` operation which does not involve copies.
79 pub fn split_off(&mut self, at: usize) -> MmapVec {
80 assert!(at <= self.range.len());
81
82 // Create a new `MmapVec` which refers to the same underlying mmap, but
83 // has a disjoint range from ours. Our own range is adjusted to be
84 // disjoint just after `ret` is created.
85 let ret = MmapVec {
86 mmap: self.mmap.clone(),
87 range: at..self.range.end,
88 };
89 self.range.end = self.range.start + at;
90 return ret;
91 }
92
93 /// Makes the specified `range` within this `mmap` to be read/execute.
94 pub unsafe fn make_executable(
95 &self,
96 range: Range<usize>,
97 enable_branch_protection: bool,
98 ) -> Result<()> {
99 assert!(range.start <= range.end);
100 assert!(range.end <= self.range.len());
101 self.mmap.make_executable(
102 range.start + self.range.start..range.end + self.range.start,
103 enable_branch_protection,
104 )
105 }
106
107 /// Makes the specified `range` within this `mmap` to be read-only.
108 pub unsafe fn make_readonly(&self, range: Range<usize>) -> Result<()> {
109 assert!(range.start <= range.end);
110 assert!(range.end <= self.range.len());
111 self.mmap
112 .make_readonly(range.start + self.range.start..range.end + self.range.start)
113 }
114
115 /// Returns the underlying file that this mmap is mapping, if present.
116 pub fn original_file(&self) -> Option<&Arc<File>> {
117 self.mmap.original_file()
118 }
119
120 /// Returns the offset within the original mmap that this `MmapVec` is
121 /// created from.
122 pub fn original_offset(&self) -> usize {
123 self.range.start
124 }
125
126 /// Returns the bounds, in host memory, of where this mmap
127 /// image resides.
128 pub fn image_range(&self) -> Range<*const u8> {
129 let base = self.as_ptr();
130 let len = self.len();
131 base..base.wrapping_add(len)
132 }
133}
134
135impl Deref for MmapVec {
136 type Target = [u8];
137
138 #[inline]
139 fn deref(&self) -> &[u8] {
140 // SAFETY: this mmap owns its own range of the underlying mmap so it
141 // should be all good-to-read.
142 unsafe { self.mmap.slice(self.range.clone()) }
143 }
144}
145
146impl DerefMut for MmapVec {
147 fn deref_mut(&mut self) -> &mut [u8] {
148 // SAFETY: The underlying mmap is protected behind an `Arc` which means
149 // there there can be many references to it. We are guaranteed, though,
150 // that each reference to the underlying `mmap` has a disjoint `range`
151 // listed that it can access. This means that despite having shared
152 // access to the mmap itself we have exclusive ownership of the bytes
153 // specified in `self.range`. This should allow us to safely hand out
154 // mutable access to these bytes if so desired.
155 unsafe {
156 let slice =
157 std::slice::from_raw_parts_mut(self.mmap.as_ptr().cast_mut(), self.mmap.len());
158 &mut slice[self.range.clone()]
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::MmapVec;
166
167 #[test]
168 fn smoke() {
169 let mut mmap = MmapVec::with_capacity(10).unwrap();
170 assert_eq!(mmap.len(), 10);
171 assert_eq!(&mmap[..], &[0; 10]);
172
173 mmap[0] = 1;
174 mmap[2] = 3;
175 assert!(mmap.get(10).is_none());
176 assert_eq!(mmap[0], 1);
177 assert_eq!(mmap[2], 3);
178 }
179
180 #[test]
181 fn split_off() {
182 let mut vec = Vec::from([1, 2, 3, 4]);
183 let mut mmap = MmapVec::from_slice(&vec).unwrap();
184 assert_eq!(&mmap[..], &vec[..]);
185 // remove nothing; vec length remains 4
186 assert_eq!(&mmap.split_off(4)[..], &vec.split_off(4)[..]);
187 assert_eq!(&mmap[..], &vec[..]);
188 // remove 1 element; vec length is now 3
189 assert_eq!(&mmap.split_off(3)[..], &vec.split_off(3)[..]);
190 assert_eq!(&mmap[..], &vec[..]);
191 // remove 2 elements; vec length is now 1
192 assert_eq!(&mmap.split_off(1)[..], &vec.split_off(1)[..]);
193 assert_eq!(&mmap[..], &vec[..]);
194 // remove last element; vec length is now 0
195 assert_eq!(&mmap.split_off(0)[..], &vec.split_off(0)[..]);
196 assert_eq!(&mmap[..], &vec[..]);
197 // nothing left to remove, but that's okay
198 assert_eq!(&mmap.split_off(0)[..], &vec.split_off(0)[..]);
199 assert_eq!(&mmap[..], &vec[..]);
200 }
201}