From 76a28ef80e00687ae59709f6c835e761a3aad5cf Mon Sep 17 00:00:00 2001 From: Will Howes Date: Tue, 9 May 2023 11:48:35 -0700 Subject: [PATCH] write completed assignment to proper fork --- src/address_space.rs | 167 +++++++++++++++++++++++++++++++++++++------ src/lib.rs | 23 +++--- 2 files changed, 162 insertions(+), 28 deletions(-) diff --git a/src/address_space.rs b/src/address_space.rs index 8bbf908..63faec5 100644 --- a/src/address_space.rs +++ b/src/address_space.rs @@ -1,8 +1,10 @@ -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 { @@ -10,12 +12,32 @@ struct MapEntry { 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, + 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, // see below for comments + mappings: Vec, // see below for comments } // comments about storing mappings @@ -34,35 +56,74 @@ 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( - &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( + &mut self, + source: Arc, offset: usize, span: usize, + flags: FlagBuilder, ) -> Result { - 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( - &self, - source: &D, + pub fn add_mapping_at( + &mut self, + source: Arc, 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. @@ -70,25 +131,70 @@ impl AddressSpace { /// # Errors /// If the mapping could not be removed. pub fn remove_mapping( - &self, - source: &D, + &mut self, + source: Arc, 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( &self, addr: VirtualAddress, - access_type: FlagBuilder - ) -> Result<(&D, usize), &str> { - todo!(); + access_type: FlagBuilder, + ) -> Result<(Arc, 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"); } } @@ -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 { @@ -218,4 +346,3 @@ impl FlagBuilder { } } } - diff --git a/src/lib.rs b/src/lib.rs index 2e974a4..f5499db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { @@ -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); } }