Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 29 additions & 45 deletions crates/runtime/src/mmap_vec.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::Mmap;
use anyhow::{Context, Result};
use std::fs::File;
use std::ops::{Deref, DerefMut, Range, RangeTo};
use std::ops::{Deref, DerefMut, Range};
use std::path::Path;
use std::sync::Arc;

Expand Down Expand Up @@ -73,36 +73,25 @@ impl MmapVec {
self.mmap.is_readonly()
}

/// "Drains" leading bytes up to the end specified in `range` from this
/// `MmapVec`, returning a separately owned `MmapVec` which retains access
/// to the bytes.
/// Splits the collection into two at the given index.
///
/// This method is similar to the `Vec` type's `drain` method, except that
/// the return value is not an iterator but rather a new `MmapVec`. The
/// purpose of this method is the ability to split-off new `MmapVec` values
/// which are sub-slices of the original one.
///
/// Once data has been drained from an `MmapVec` it is no longer accessible
/// from the original `MmapVec`, it's only accessible from the returned
/// `MmapVec`. In other words ownership of the drain'd bytes is returned
/// through the `MmapVec` return value.
///
/// This `MmapVec` will shrink by `range.end` bytes, and it will only refer
/// to the bytes that come after the drain range.
/// Returns a separate `MmapVec` which shares the underlying mapping, but
/// only has access to elements in the range `[at, len)`. After the call,
/// the original `MmapVec` will be left with access to the elements in the
/// range `[0, at)`.
///
/// This is an `O(1)` operation which does not involve copies.
pub fn drain(&mut self, range: RangeTo<usize>) -> MmapVec {
let amt = range.end;
assert!(amt <= (self.range.end - self.range.start));
pub fn split_off(&mut self, at: usize) -> MmapVec {
assert!(at <= self.range.len());

// Create a new `MmapVec` which refers to the same underlying mmap, but
// has a disjoint range from ours. Our own range is adjusted to be
// disjoint just after `ret` is created.
let ret = MmapVec {
mmap: self.mmap.clone(),
range: self.range.start..self.range.start + amt,
range: at..self.range.end,
};
self.range.start += amt;
self.range.end = self.range.start + at;
return ret;
}

Expand Down Expand Up @@ -173,29 +162,24 @@ mod tests {
}

#[test]
fn drain() {
let mut mmap = MmapVec::from_slice(&[1, 2, 3, 4]).unwrap();
assert_eq!(mmap.len(), 4);
assert!(mmap.drain(..0).is_empty());
assert_eq!(mmap.len(), 4);
let one = mmap.drain(..1);
assert_eq!(one.len(), 1);
assert_eq!(one[0], 1);
assert_eq!(mmap.len(), 3);
assert_eq!(&mmap[..], &[2, 3, 4]);
drop(one);
assert_eq!(mmap.len(), 3);

let two = mmap.drain(..2);
assert_eq!(two.len(), 2);
assert_eq!(two[0], 2);
assert_eq!(two[1], 3);
assert_eq!(mmap.len(), 1);
assert_eq!(mmap[0], 4);
drop(two);
assert!(mmap.drain(..0).is_empty());
assert!(mmap.drain(..1).len() == 1);
assert!(mmap.is_empty());
assert!(mmap.drain(..0).is_empty());
fn split_off() {
let mut vec = Vec::from([1, 2, 3, 4]);
let mut mmap = MmapVec::from_slice(&vec).unwrap();
assert_eq!(&mmap[..], &vec[..]);
// remove nothing; vec length remains 4
assert_eq!(&mmap.split_off(4)[..], &vec.split_off(4)[..]);
assert_eq!(&mmap[..], &vec[..]);
// remove 1 element; vec length is now 3
assert_eq!(&mmap.split_off(3)[..], &vec.split_off(3)[..]);
assert_eq!(&mmap[..], &vec[..]);
// remove 2 elements; vec length is now 1
assert_eq!(&mmap.split_off(1)[..], &vec.split_off(1)[..]);
assert_eq!(&mmap[..], &vec[..]);
// remove last element; vec length is now 0
assert_eq!(&mmap.split_off(0)[..], &vec.split_off(0)[..]);
assert_eq!(&mmap[..], &vec[..]);
// nothing left to remove, but that's okay
assert_eq!(&mmap.split_off(0)[..], &vec.split_off(0)[..]);
assert_eq!(&mmap[..], &vec[..]);
}
}
10 changes: 5 additions & 5 deletions crates/wasmtime/src/module/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,11 @@ impl<'a> SerializedModule<'a> {
// First validate that this is at least somewhat an elf file within
// `mmap` and additionally skip to the end of the elf file to find our
// metadata.
let elf = take_first_elf(&mut mmap)?;
let metadata = data_after_elf(&mut mmap)?;

// The metadata has a few guards up front which we process first, and
// eventually this bottoms out in a `bincode::deserialize` call.
let metadata = mmap
let metadata = metadata
.strip_prefix(HEADER)
.ok_or_else(|| anyhow!("bytes are not a compatible serialized wasmtime module"))?;
if metadata.is_empty() {
Expand Down Expand Up @@ -309,13 +309,13 @@ impl<'a> SerializedModule<'a> {
.context("deserialize compilation artifacts")?;

return Ok(SerializedModule {
artifacts: MyCow::Owned(elf),
artifacts: MyCow::Owned(mmap),
metadata,
});

/// This function will return the trailing data behind the ELF file
/// parsed from `data` which is where we find our metadata section.
fn take_first_elf(mmap: &mut MmapVec) -> Result<MmapVec> {
fn data_after_elf(mmap: &mut MmapVec) -> Result<MmapVec> {
use object::NativeEndian as NE;
// There's not actually a great utility for figuring out where
// the end of an ELF file is in the `object` crate. In lieu of that
Expand All @@ -335,7 +335,7 @@ impl<'a> SerializedModule<'a> {
.section_headers(NE, data)
.context("failed to read section headers")?;
let range = subslice_range(object::bytes_of_slice(sections), data);
Ok(mmap.drain(..range.end))
Ok(mmap.split_off(range.end))
}
}

Expand Down