Skip to content
Open
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
167 changes: 147 additions & 20 deletions src/address_space.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,43 @@
use std::collections::LinkedList;
use std::sync::Arc;

use crate::data_source::DataSource;

pub const PAGE_SIZE: usize = 4096;
pub const VADDR_MAX: usize = (1 << 38) - 1;

type VirtualAddress = usize;

struct MapEntry {
source: Arc<dyn DataSource>,
offset: usize,
span: usize,
addr: usize,
flags: FlagBuilder,
}

impl MapEntry {
#[must_use] // <- not using return value of "new" doesn't make sense, so warn
pub fn new(
source: Arc<dyn DataSource>,
offset: usize,
span: usize,
addr: usize,
flags: FlagBuilder,
) -> MapEntry {
MapEntry {
source: source.clone(),
offset,
span,
addr,
flags,
}
}
}

/// An address space.
pub struct AddressSpace {
name: String,
mappings: LinkedList<MapEntry>, // see below for comments
mappings: Vec<MapEntry>, // see below for comments
}

// comments about storing mappings
Expand All @@ -34,61 +56,145 @@ impl AddressSpace {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
mappings: LinkedList::new(),
}
mappings: Vec::new(), // <- here I changed from LinkedList, for reasons
} // I encourage you to try other sparse representations - trees, DIY linked lists, ...
}

/// Add a mapping from a `DataSource` into this `AddressSpace`.
///
/// # Errors
/// If the desired mapping is invalid.
pub fn add_mapping<D: DataSource>(
&self,
source: &D,
/// TODO: how does our test in lib.rs succeed?
// pub fn add_mapping<'a, D: DataSource + 'a>(
// &'a mut self,
pub fn add_mapping<D: DataSource + 'static>(
&mut self,
source: Arc<D>,
offset: usize,
span: usize,
flags: FlagBuilder,
) -> Result<VirtualAddress, &str> {
todo!()
let mut addr_iter = PAGE_SIZE; // let's not map page 0
let mut gap;
for mapping in &self.mappings {
gap = mapping.addr - addr_iter;
if gap > span + 2 * PAGE_SIZE {
break;
}
addr_iter = mapping.addr + mapping.span;
}
if addr_iter + span + 2 * PAGE_SIZE < VADDR_MAX {
let mapping_addr = addr_iter + PAGE_SIZE;
let new_mapping = MapEntry::new(source, offset, span, mapping_addr, flags);
self.mappings.push(new_mapping);
self.mappings.sort_by(|a, b| a.addr.cmp(&b.addr));
return Ok(mapping_addr);
}
Err("out of address space!")
}

/// Add a mapping from `DataSource` into this `AddressSpace` starting at a specific address.
///
/// # Errors
/// If there is insufficient room subsequent to `start`.
pub fn add_mapping_at<D: DataSource>(
&self,
source: &D,
pub fn add_mapping_at<D: DataSource + 'static>(
&mut self,
source: Arc<D>,
offset: usize,
span: usize,
start: VirtualAddress,
flags: FlagBuilder,
) -> Result<(), &str> {
todo!()
if !flags.read {
return Err("Invalid permissions");
}
for mapping in &self.mappings {
if start <= mapping.addr {
if span + PAGE_SIZE < mapping.addr - start {
break;
}
return Err("Address space is too small to complete request.");
}
}
if start + span + PAGE_SIZE >= VADDR_MAX {
Err("Address space is too small to complete request.")
} else {
let new_mapping = MapEntry::new(source, offset, span, start, flags);
self.mappings.push(new_mapping);
self.mappings.sort_by(|a, b| a.addr.cmp(&b.addr));
return Ok(());
}
}

/// Remove the mapping to `DataSource` that starts at the given address.
///
/// # Errors
/// If the mapping could not be removed.
pub fn remove_mapping<D: DataSource>(
&self,
source: &D,
&mut self,
source: Arc<D>,
start: VirtualAddress,
) -> Result<(), &str> {
todo!()
let desired_entry = self.get_mapping_for_addr(start).unwrap();
let len = &self.mappings.len();

let mut i: usize = 0;
let mut search_success: bool = false;

for mapping in &self.mappings {
if mapping.addr == desired_entry.addr {
search_success = true;
break;
}
i += 1;
}

if search_success {
self.mappings.remove(i);
} else {
return Err("Could not locate mapping during removal");
}

if self.mappings.len() != len - 1 {
return Err("Mapping failed to be removed");
}

drop(source);

return Ok(());
}

/// Look up the DataSource and offset within that DataSource for a
/// VirtualAddress / AccessType in this AddressSpace
///
///
/// # Errors
/// If this VirtualAddress does not have a valid mapping in &self,
/// or if this AccessType is not permitted by the mapping
pub fn get_source_for_addr<D: DataSource>(
&self,
addr: VirtualAddress,
access_type: FlagBuilder
) -> Result<(&D, usize), &str> {
todo!();
access_type: FlagBuilder,
) -> Result<(Arc<dyn DataSource>, usize), &str> {
let desired_mapping = self.get_mapping_for_addr(addr).unwrap();
return Ok((
desired_mapping.source.clone(),
desired_mapping.offset + addr,
));
}

/// Helper function for looking up mappings
fn get_mapping_for_addr(&self, addr: VirtualAddress) -> Result<&MapEntry, &str> {
for mapping in &self.mappings {
let end_of_mapping = mapping.addr + mapping.span;
if mapping.addr + mapping.span < addr {
continue;
} else if addr <= end_of_mapping && mapping.addr <= addr {
return Ok(mapping);
} else if addr < mapping.addr {
return Err("VirtualAddress is an invalid mapping");
}
}
return Err("VirtualAddress is an invalid mapping");
}
}

Expand Down Expand Up @@ -116,6 +222,28 @@ pub struct FlagBuilder {
shared: bool,
}

impl FlagBuilder {
pub fn check_access_perms(&self, access_perms: FlagBuilder) -> bool {
if access_perms.read && !self.read
|| access_perms.write && !self.write
|| access_perms.execute && !self.execute
{
return false;
}
true
}

pub fn is_valid(&self) -> bool {
if self.private && self.shared {
return false;
}
if self.cow && self.write {
// for COW to work, write needs to be off until after the copy
return false;
}
return true;
}
}
/// Create a constructor and toggler for a `FlagBuilder` object. Will capture attributes, including documentation
/// comments and apply them to the generated constructor.
macro_rules! flag {
Expand Down Expand Up @@ -218,4 +346,3 @@ impl FlagBuilder {
}
}
}

23 changes: 15 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ mod address_space;
mod cacher;
mod data_source;

pub use address_space::AddressSpace;
pub use address_space::{AddressSpace, FlagBuilder};
pub use data_source::{DataSource, FileDataSource};
use std::sync::Arc; // <- will have to make Arc ourselves for #no_std
// TODO: why does rustfmt say this is unused, but if I leave it out, undefined?

#[cfg(test)]
mod tests {
Expand All @@ -23,18 +25,23 @@ mod tests {
// test if mapping has been added
#[test]
fn test_add_mapping() {
let addr_space = AddressSpace::new("Test address space");
let mut addr_space = AddressSpace::new("Test address space");
let data_source: FileDataSource = FileDataSource::new("Cargo.toml").unwrap();
let offset: usize = 0;
let length: usize = 1;
let read_flags = FlagBuilder::new().toggle_read();

let addr = addr_space.add_mapping(&data_source, offset, length).unwrap();
let ds_arc = Arc::new(data_source);

let addr = addr_space
.add_mapping(ds_arc.clone(), offset, length, read_flags)
.unwrap();
assert!(addr != 0);

// we should move these tests into addr_space, since they access non-public internals of the structure:
// assert_eq!(addr_space.mappings.is_empty(), false);
// assert_eq!(addr_space.mappings.front().source, Some(&data_source));
// assert_eq!(addr_space.mappings.front().offset, offset);
// assert_eq!(addr_space.mappings.front().span, length);
let addr2 = addr_space
.add_mapping(ds_arc.clone(), address_space::PAGE_SIZE, 0, read_flags)
.unwrap();
assert!(addr2 != 0);
assert!(addr2 != addr);
}
}