From 78e4a468c1c28adf03592ac0077c07dc177f0bf2 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 24 Mar 2022 22:13:34 +0200 Subject: [PATCH 1/6] build: bump to 1.57 Signed-off-by: Jarkko Sakkinen --- .github/workflows/test.yml | 2 +- Cargo.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f4dc006..b10a539 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,7 @@ jobs: - nightly - beta - stable - - 1.56.0 + - 1.57.0 profile: - name: debug - name: release diff --git a/Cargo.toml b/Cargo.toml index ad09017..a92343d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ homepage = "https://github.com/jarkkojs/mmledger" repository = "https://github.com/jarkkojs/mmledger" description = "A ledger for confidential computing (CC) shims for tracking memory management system calls" edition = "2021" +rust-version = "1.57" include = [ "**/*.rs", "Cargo.toml", From 9d957cfb00bbfff578005ab7b0ae6405e07319cb Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 24 Mar 2022 12:44:06 +0200 Subject: [PATCH 2/6] cleanup: removed old tests Signed-off-by: Jarkko Sakkinen --- src/lib.rs | 195 ----------------------------------------------------- 1 file changed, 195 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 171e00c..9a62046 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -387,199 +387,4 @@ mod tests { assert_eq!(ledger.records[1], PREV); assert_eq!(ledger.records[2], MERGED); } - - /* - use std::mem::{align_of, size_of}; - - use super::*; - - const MEMORY_MAP_ADDRESS: Address = - unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - const MEMORY_MAP_SIZE: usize = 3 * Page::SIZE; - - #[test] - fn addr_region_equal() { - const A: Address = unsafe { Address::new_unchecked(Page::SIZE as *mut c_void) }; - const B: Address = unsafe { Address::new_unchecked(Page::SIZE as *mut c_void) }; - - assert_eq!(Region::new(A, Page::SIZE), Region::new(B, Page::SIZE),); - } - - #[test] - fn addr_region_not_equal() { - const A: Address = unsafe { Address::new_unchecked(Page::SIZE as *mut c_void) }; - const B: Address = unsafe { Address::new_unchecked(MEMORY_MAP_SIZE as *mut c_void) }; - - assert!(Region::new(A, Page::SIZE) != Region::new(B, Page::SIZE)); - } - - #[test] - fn alloc_region_success() { - const A: Address = unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - const B: Address = unsafe { Address::new_unchecked((4 * Page::SIZE) as *mut c_void) }; - - let mut m: AddressSpace<2> = AddressSpace::new(MEMORY_MAP_ADDRESS, MEMORY_MAP_SIZE); - let region_a = Region::new(A, Page::SIZE); - let region_b = Region::new(B, Page::SIZE); - - m.insert_region(region_a, AddressSpaceFlags::empty()) - .unwrap(); - m.insert_region(region_b, AddressSpaceFlags::empty()) - .unwrap(); - - let result = match m.allocate_region(Page::SIZE, AddressSpaceFlags::DRY_RUN) { - Ok(r) => r, - _ => panic!(), - }; - - assert_eq!(result, Region::new(MEMORY_MAP_ADDRESS, 3 * Page::SIZE)); - } - - #[test] - fn alloc_region_failure() { - const A: Address = unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - const B: Address = unsafe { Address::new_unchecked((4 * Page::SIZE) as *mut c_void) }; - const C: Address = unsafe { Address::new_unchecked((3 * Page::SIZE) as *mut c_void) }; - - let mut m: AddressSpace<2> = AddressSpace::new(MEMORY_MAP_ADDRESS, MEMORY_MAP_SIZE); - let region_a = Region::new(A, Page::SIZE); - let region_b = Region::new(B, Page::SIZE); - let region_c = Region::new(C, Page::SIZE); - - m.insert_region(region_a, AddressSpaceFlags::empty()) - .unwrap(); - m.insert_region(region_b, AddressSpaceFlags::empty()) - .unwrap(); - m.insert_region(region_c, AddressSpaceFlags::empty()) - .unwrap(); - - match m.allocate_region(Page::SIZE, AddressSpaceFlags::DRY_RUN) { - Err(AddressSpaceError::OutOfSpace) => (), - _ => panic!(), - } - } - - #[test] - fn extend_region() { - const A: Address = unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - const B: Address = unsafe { Address::new_unchecked((4 * Page::SIZE) as *mut c_void) }; - const E: Address = unsafe { Address::new_unchecked((3 * Page::SIZE) as *mut c_void) }; - - let mut m: AddressSpace<1> = AddressSpace::new(MEMORY_MAP_ADDRESS, MEMORY_MAP_SIZE); - let region_a = Region::new(A, Page::SIZE); - let expected = Region::new(E, Page::SIZE); - - println!("{:?}", region_a); - - m.insert_region(region_a, AddressSpaceFlags::empty()) - .unwrap(); - let result = match m.extend_region(B) { - Ok(region) => region, - _ => panic!(), - }; - - assert_eq!(result, expected); - } - - #[test] - fn insert_region() { - const A: Address = unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - - let mut m: AddressSpace<1> = AddressSpace::new(MEMORY_MAP_ADDRESS, MEMORY_MAP_SIZE); - let region = Region::new(A, Page::SIZE); - - let region = match m.insert_region(region, AddressSpaceFlags::empty()) { - Ok(region) => region, - _ => panic!(), - }; - - assert_eq!(region, Region::new(A, Page::SIZE)); - } - - #[test] - fn insert_adjacent() { - const A: Address = unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - const B: Address = unsafe { Address::new_unchecked((3 * Page::SIZE) as *mut c_void) }; - - let mut m: AddressSpace<2> = AddressSpace::new(MEMORY_MAP_ADDRESS, MEMORY_MAP_SIZE); - let region_a = Region::new(A, Page::SIZE); - let region_b = Region::new(B, Page::SIZE); - - m.insert_region(region_a, AddressSpaceFlags::empty()) - .unwrap(); - - let region = match m.insert_region(region_b, AddressSpaceFlags::DRY_RUN) { - Ok(region) => region, - _ => panic!(), - }; - - assert_eq!(region, Region::new(A, 2 * Page::SIZE)); - } - - #[test] - fn insert_after_memory_map() { - const A: Address = unsafe { Address::new_unchecked((5 * Page::SIZE) as *mut c_void) }; - - let mut m: AddressSpace<2> = AddressSpace::new(MEMORY_MAP_ADDRESS, MEMORY_MAP_SIZE); - let region_a = Region::new(A, Page::SIZE); - - match m.insert_region(region_a, AddressSpaceFlags::DRY_RUN) { - Err(AddressSpaceError::Overflow) => (), - _ => panic!(), - } - } - - #[test] - fn insert_intersects() { - const A: Address = unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - const B: Address = unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - - let mut m: AddressSpace<2> = AddressSpace::new(MEMORY_MAP_ADDRESS, MEMORY_MAP_SIZE); - let region_a = Region::new(A, Page::SIZE); - let region_b = Region::new(B, Page::SIZE); - - m.insert_region(region_a, AddressSpaceFlags::empty()) - .unwrap(); - match m.insert_region(region_b, AddressSpaceFlags::DRY_RUN) { - Err(AddressSpaceError::Overlap) => (), - _ => panic!(), - } - } - - #[test] - fn insert_not_intersects() { - const A: Address = unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - const B: Address = unsafe { Address::new_unchecked((4 * Page::SIZE) as *mut c_void) }; - - let mut m: AddressSpace<2> = AddressSpace::new(MEMORY_MAP_ADDRESS, MEMORY_MAP_SIZE); - let region_a = Region::new(A, Page::SIZE); - let region_b = Region::new(B, Page::SIZE); - - m.insert_region(region_a, AddressSpaceFlags::empty()) - .unwrap(); - let region_c = match m.insert_region(region_b, AddressSpaceFlags::DRY_RUN) { - Ok(region) => region, - _ => panic!(), - }; - - assert_eq!(region_c, region_b); - } - - #[test] - fn insert_overflow() { - const A: Address = unsafe { Address::new_unchecked((2 * Page::SIZE) as *mut c_void) }; - const B: Address = unsafe { Address::new_unchecked((4 * Page::SIZE) as *mut c_void) }; - - let mut m: AddressSpace<1> = AddressSpace::new(MEMORY_MAP_ADDRESS, MEMORY_MAP_SIZE); - let region_a = Region::new(A, Page::SIZE); - let region_b = Region::new(B, Page::SIZE); - - m.insert_region(region_a, AddressSpaceFlags::empty()) - .unwrap(); - match m.insert_region(region_b, AddressSpaceFlags::DRY_RUN) { - Err(AddressSpaceError::OutOfCapacity) => (), - _ => panic!(), - } - } - */ } From 713e81ba583ab447bdda3521bca478eabc7a5d06 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 24 Mar 2022 13:14:23 +0200 Subject: [PATCH 3/6] tests: allow `std` for tests Signed-off-by: Jarkko Sakkinen --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 9a62046..d1d247b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! A ledger for memory mappings. -#![no_std] +#![cfg_attr(not(test), no_std)] #![deny(clippy::all)] #![deny(missing_docs)] #![forbid(unsafe_code)] From f25c048152f2277f02f6e66938309cd5239132c0 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 24 Mar 2022 05:50:00 +0200 Subject: [PATCH 4/6] feat: introduce `Region` Closes: #8 Signed-off-by: Jarkko Sakkinen --- src/lib.rs | 391 +++++++++++++++++++---------------------------------- 1 file changed, 137 insertions(+), 254 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d1d247b..59e8318 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,48 +10,33 @@ use core::cmp::Ordering; use lset::{Empty, Line, Span}; use primordial::{Address, Offset, Page}; -bitflags::bitflags! { - /// Memory access permissions. - #[derive(Default)] - #[repr(transparent)] - pub struct Access: usize { - /// Read access - const READ = 1 << 0; - - /// Write access - const WRITE = 1 << 0; - - /// Execute access - const EXECUTE = 1 << 0; - } -} - -/// A ledger record. +/// A range of tagged address space in a ledger. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct Record { - /// The covered region of memory. - pub region: Line>, - - /// The access permissions. - pub access: Access, - - length: usize, +#[repr(C, align(32))] +pub struct Region { + /// Address range + pub addresses: Line>, + /// Access token + pub token: u64, + reserved: u64, } -impl Record { - const EMPTY: Record = Record { - region: Line::new(Address::NULL, Address::NULL), - access: Access::empty(), - length: 0, - }; - - fn new(region: Line>, access: Access) -> Self { - Record { - region, - access, - length: 0, +impl Region { + #[inline] + /// Create a new instance. + pub const fn new(addresses: Line>, token: u64) -> Self { + Self { + addresses, + token, + reserved: 0, } } + + #[inline] + /// Create an empty instance. + pub const fn empty() -> Self { + Self::new(Line::new(Address::NULL, Address::NULL), 0) + } } /// Ledger error conditions. @@ -63,121 +48,106 @@ pub enum Error { /// No space for the region OutOfSpace, - /// Not inside the address space - Overflow, - - /// Overlap with the existing regions + /// Overlapping with the existing regions Overlap, - /// Invalid region + /// Invalid region given as input InvalidRegion, } -/// A virtual memory map ledger. -// -// Developer Note: the first record is reserved for the ledger bounds. We -// structure it this way so that the user of the `Ledger` type has -// fine-grained controls over allocation. For example, to allocate a 4k page, -// the user can instantiate as `Ledger::<128>::new(..)`. +/// Maintains a log of reserved address regions. The header of the ledger and +/// each region are 32 bytes, and they are also aligned to 32 bytes. To get a +/// ledger of which total size is a power of two, pick N = M - 1, where M is a +/// power of two. #[derive(Clone, Debug)] +#[repr(C, align(32))] pub struct Ledger { - records: [Record; N], + addresses: Line>, + len: usize, + reserved: u64, + regions: [Region; N], } impl Ledger { - /// Sort the records. + /// Sort the regions. fn sort(&mut self) { - self.records_mut().sort_unstable_by(|l, r| { - if l.region == r.region { + self.regions_mut().sort_unstable_by(|l, r| { + if l.addresses == r.addresses { Ordering::Equal - } else if l.region.is_empty() { + } else if l.addresses.is_empty() { Ordering::Greater - } else if r.region.is_empty() { + } else if r.addresses.is_empty() { Ordering::Less } else { - l.region.start.cmp(&r.region.start) + l.addresses.start.cmp(&r.addresses.start) } }) } /// Create a new instance. - pub const fn new(region: Line>) -> Self { - let mut records = [Record::EMPTY; N]; - records[0].region = region; - Self { records } + pub const fn new(addresses: Line>) -> Self { + Self { + addresses, + len: 0, + reserved: 0, + regions: [Region::empty(); N], + } } - /// Get an immutable view of the records. - pub fn records(&self) -> &[Record] { - let used = self.records[0].length; - &self.records[1..][..used] + /// Get an immutable view of the regions. + pub fn regions(&self) -> &[Region] { + &self.regions[..self.len] } - /// Get a mutable view of the records. - /// - /// This function MUST NOT be public. - fn records_mut(&mut self) -> &mut [Record] { - let used = self.records[0].length; - &mut self.records[1..][..used] + /// Get a mutable view of the regions. + fn regions_mut(&mut self) -> &mut [Region] { + &mut self.regions[..self.len] } - /// Insert a new record into the ledger. - pub fn insert( - &mut self, - region: impl Into>>, - access: impl Into>, - commit: bool, - ) -> Result<(), Error> { - // Make sure the record is valid. - let record = Record::new(region.into(), access.into().unwrap_or_default()); - if record.region.start >= record.region.end { + /// Insert a new region into the ledger. + pub fn insert(&mut self, region: Region) -> Result<(), Error> { + if region.addresses.start >= region.addresses.end { return Err(Error::InvalidRegion); } - // Make sure the record fits in our adress space. - let region = self.records[0].region; - if record.region.start < region.start || record.region.end > region.end { - return Err(Error::Overflow); + // Make sure the region fits in our adress space. + if region.addresses.start < self.addresses.start + || region.addresses.end > self.addresses.end + { + return Err(Error::InvalidRegion); } - // Loop over the records looking for merges. - let mut iter = self.records_mut().iter_mut().peekable(); + // Loop over the regions looking for merges. + let mut iter = self.regions_mut().iter_mut().peekable(); while let Some(prev) = iter.next() { - if prev.region.intersection(record.region).is_some() { + if prev.addresses.intersection(region.addresses).is_some() { return Err(Error::Overlap); } if let Some(next) = iter.peek() { - if next.region.intersection(record.region).is_some() { + if next.addresses.intersection(region.addresses).is_some() { return Err(Error::Overlap); } } - // Potentially merge with the `prev` slot. - if prev.access == record.access && prev.region.end == record.region.start { - if commit { - prev.region.end = record.region.end; - } - + // Merge previous. + if prev.token == region.token && prev.addresses.end == region.addresses.start { + prev.addresses.end = region.addresses.end; return Ok(()); } - // Potentially merge with the `prev` slot + // Merge next. if let Some(next) = iter.peek_mut() { - if next.access == record.access && next.region.start == record.region.end { - if commit { - next.region.start = record.region.start; - } - + if next.token == region.token && next.addresses.start == region.addresses.end { + next.addresses.start = region.addresses.start; return Ok(()); } } } - // If there is room to append a new record. - if self.records[0].length + 2 <= self.records.len() { - self.records[0].length += 1; - self.records[self.records[0].length] = record; + if self.len < self.regions.len() { + self.regions[self.len] = region; + self.len += 1; self.sort(); return Ok(()); } @@ -185,48 +155,35 @@ impl Ledger { Err(Error::OutOfCapacity) } - /// Find space for a free region. + /// Find space for a region. pub fn find_free( &self, len: Offset, front: bool, ) -> Result>, Error> { - let region = self.records[0].region; - - let start = Record { - region: Line::new(region.start, region.start), - ..Default::default() - }; - - let end = Record { - region: Line::new(region.end, region.end), - ..Default::default() - }; - - // Synthesize a starting window. - let first = [start, *self.records().first().unwrap_or(&end)]; - - // Synthesize an ending window. - let last = [*self.records().last().unwrap_or(&start), end]; + let start = Region::new(Line::new(self.addresses.start, self.addresses.start), 0); + let end = Region::new(Line::new(self.addresses.end, self.addresses.end), 0); + let first = [start, *self.regions().first().unwrap_or(&end)]; + let last = [*self.regions().last().unwrap_or(&start), end]; // Chain everything together. let mut iter = first .windows(2) - .chain(self.records().windows(2)) + .chain(self.regions().windows(2)) .chain(last.windows(2)); // Iterate through the windows. if front { while let Some([l, r]) = iter.next() { - if r.region.end - l.region.start > len { - return Ok(Span::new(l.region.end, len).into()); + if r.addresses.end - l.addresses.start > len { + return Ok(Span::new(l.addresses.end, len).into()); } } } else { let mut iter = iter.rev(); while let Some([l, r]) = iter.next() { - if r.region.end - l.region.start > len { - return Ok(Span::new(r.region.start - len, len).into()); + if r.addresses.end - l.addresses.start > len { + return Ok(Span::new(r.addresses.start - len, len).into()); } } } @@ -239,152 +196,78 @@ impl Ledger { mod tests { use super::*; - use core::mem::{align_of, size_of}; - - const PREV: Record = Record { - region: Line { - start: Address::new(0x4000usize), - end: Address::new(0x5000usize), - }, - access: Access::empty(), - length: 0, - }; - - const NEXT: Record = Record { - region: Line { - start: Address::new(0x8000), - end: Address::new(0x9000), - }, - access: Access::empty(), - length: 0, - }; - - const INDX: Record = Record { - region: Line { - start: Address::new(0x1000), - end: Address::new(0x10000), - }, - access: Access::empty(), - length: 2, - }; - - const LEDGER: Ledger<3> = Ledger { - records: [INDX, PREV, NEXT], - }; - - #[test] - fn record_size_align() { - assert_eq!(size_of::(), size_of::() * 4); - assert_eq!(align_of::(), align_of::()); - } + const LIMITS: Line> = + Line::new(Address::new(0x1000), Address::new(0x10000)); #[test] fn insert() { - let start = Address::from(0x1000usize).lower(); - let end = Address::from(0x10000usize).lower(); - let mut ledger = Ledger::<8>::new(Line::new(start, end)); - - let region = Line { - start: Address::from(0xe000usize).lower(), - end: Address::from(0x10000usize).lower(), - }; - - assert_eq!(ledger.records(), &[]); - ledger.insert(region, None, true).unwrap(); - assert_eq!(ledger.records(), &[Record::new(region, Access::empty())]); + const X: Line> = + Line::new(Address::new(0xe000), Address::new(0x10000)); + + let mut ledger: Ledger<1> = Ledger::new(LIMITS); + assert_eq!(ledger.len, 0); + ledger.insert(Region::new(X, 0)).unwrap(); + assert_eq!(ledger.regions(), &[Region::new(X, 0)]); } #[test] fn find_free_front() { - let start = Address::from(0x1000).lower(); - let end = Address::from(0x10000).lower(); - let mut ledger = Ledger::<8>::new(Line::new(start, end)); - - let region = ledger.find_free(Offset::from_items(2), true).unwrap(); - let answer = Line { - start: Address::from(0x1000).lower(), - end: Address::from(0x3000).lower(), - }; - assert_eq!(region, answer); - - ledger.insert(answer, None, true).unwrap(); - - let region = ledger.find_free(Offset::from_items(2), true).unwrap(); - let answer = Line { - start: Address::from(0x3000).lower(), - end: Address::from(0x5000).lower(), - }; - assert_eq!(region, answer); + const D: Offset = Offset::from_items(2); + const A: Line> = Line::new(Address::new(0x1000), Address::new(0x3000)); + const B: Line> = Line::new(Address::new(0x3000), Address::new(0x5000)); + + let mut ledger: Ledger<8> = Ledger::new(LIMITS); + assert_eq!(ledger.find_free(D, true).unwrap(), A); + ledger.insert(Region::new(A, 0)).unwrap(); + assert_eq!(ledger.find_free(D, true).unwrap(), B); } #[test] fn find_free_back() { - let start = Address::from(0x1000).lower(); - let end = Address::from(0x10000).lower(); - let mut ledger = Ledger::<8>::new(Line::new(start, end)); - - let region = ledger.find_free(Offset::from_items(2), false).unwrap(); - let answer = Line { - start: Address::from(0xe000).lower(), - end: Address::from(0x10000).lower(), - }; - assert_eq!(region, answer); - - ledger.insert(answer, None, true).unwrap(); - - let region = ledger.find_free(Offset::from_items(2), false).unwrap(); - let answer = Line { - start: Address::from(0xc000).lower(), - end: Address::from(0xe000).lower(), - }; - assert_eq!(region, answer); + const D: Offset = Offset::from_items(2); + const A: Line> = + Line::new(Address::new(0xe000), Address::new(0x10000)); + const B: Line> = Line::new(Address::new(0xc000), Address::new(0xe000)); + + let mut ledger: Ledger<8> = Ledger::new(LIMITS); + assert_eq!(ledger.find_free(D, false).unwrap(), A); + ledger.insert(Region::new(A, 0)).unwrap(); + assert_eq!(ledger.find_free(D, false).unwrap(), B); } #[test] fn merge_after() { - const REGION: Line> = Line { - start: Address::new(0x5000), - end: Address::new(0x6000), - }; - - const MERGED: Record = Record { - region: Line { - start: Address::new(0x4000), - end: Address::new(0x6000), - }, - access: Access::empty(), - length: 0, - }; - - let mut ledger = LEDGER.clone(); - ledger.insert(REGION, Access::empty(), true).unwrap(); - - assert_eq!(ledger.records[0].length, 2); - assert_eq!(ledger.records[1], MERGED); - assert_eq!(ledger.records[2], NEXT); + const A: Line> = Line::new(Address::new(0x4000), Address::new(0x5000)); + const B: Line> = Line::new(Address::new(0x8000), Address::new(0x9000)); + + const X: Line> = Line::new(Address::new(0x5000), Address::new(0x6000)); + const Y: Line> = Line::new(Address::new(0x4000), Address::new(0x6000)); + + let mut ledger: Ledger<8> = Ledger::new(LIMITS); + ledger.insert(Region::new(A, 0)).unwrap(); + ledger.insert(Region::new(B, 0)).unwrap(); + ledger.insert(Region::new(X, 0)).unwrap(); + + assert_eq!(ledger.len, 2); + assert_eq!(ledger.regions[0], Region::new(Y, 0)); + assert_eq!(ledger.regions[1].addresses, B); } #[test] fn merge_before() { - const REGION: Line> = Line { - start: Address::new(0x7000), - end: Address::new(0x8000), - }; - - const MERGED: Record = Record { - region: Line { - start: Address::new(0x7000), - end: Address::new(0x9000), - }, - access: Access::empty(), - length: 0, - }; - - let mut ledger = LEDGER.clone(); - ledger.insert(REGION, Access::empty(), true).unwrap(); - - assert_eq!(ledger.records[0].length, 2); - assert_eq!(ledger.records[1], PREV); - assert_eq!(ledger.records[2], MERGED); + const A: Line> = Line::new(Address::new(0x4000), Address::new(0x5000)); + const B: Line> = Line::new(Address::new(0x8000), Address::new(0x9000)); + + const X: Line> = Line::new(Address::new(0x7000), Address::new(0x8000)); + const Y: Line> = Line::new(Address::new(0x7000), Address::new(0x9000)); + + let mut ledger: Ledger<8> = Ledger::new(LIMITS); + ledger.insert(Region::new(A, 0)).unwrap(); + ledger.insert(Region::new(B, 0)).unwrap(); + ledger.insert(Region::new(X, 0)).unwrap(); + + assert_eq!(ledger.len, 2); + assert_eq!(ledger.regions[0].addresses, A); + assert_eq!(ledger.regions[1], Region::new(Y, 0)); } } From fd16d734a00c83ba03e40619a6ba8f5d3417a141 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 24 Mar 2022 14:47:50 +0200 Subject: [PATCH 5/6] refactor: split `find_free()` to `find_free_{front,back}()` Closes: #11 Signed-off-by: Jarkko Sakkinen --- src/lib.rs | 86 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 59e8318..73ed81d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ pub enum Error { OutOfCapacity, /// No space for the region - OutOfSpace, + NoSpace, /// Overlapping with the existing regions Overlap, @@ -155,40 +155,62 @@ impl Ledger { Err(Error::OutOfCapacity) } - /// Find space for a region. - pub fn find_free( + /// Find free space from front. + pub fn find_free_front( &self, len: Offset, - front: bool, ) -> Result>, Error> { - let start = Region::new(Line::new(self.addresses.start, self.addresses.start), 0); - let end = Region::new(Line::new(self.addresses.end, self.addresses.end), 0); - let first = [start, *self.regions().first().unwrap_or(&end)]; - let last = [*self.regions().last().unwrap_or(&start), end]; - - // Chain everything together. - let mut iter = first - .windows(2) - .chain(self.regions().windows(2)) - .chain(last.windows(2)); - - // Iterate through the windows. - if front { - while let Some([l, r]) = iter.next() { - if r.addresses.end - l.addresses.start > len { - return Ok(Span::new(l.addresses.end, len).into()); - } + if len.bytes() == 0 || len > self.addresses.end - self.addresses.start { + return Err(Error::InvalidRegion); + } + + if self.len == 0 { + return Ok(Span::new(self.addresses.start, len).into()); + } + + let mut prev: Address = self.addresses.start; + for i in 0..self.len { + let next = self.regions[i].addresses.start; + if len <= next - prev { + return Ok(Span::new(prev, len).into()); } - } else { - let mut iter = iter.rev(); - while let Some([l, r]) = iter.next() { - if r.addresses.end - l.addresses.start > len { - return Ok(Span::new(r.addresses.start - len, len).into()); - } + prev = self.regions[i].addresses.end; + } + + if len <= self.addresses.end - prev { + return Ok(Span::new(prev, len).into()); + } + + Err(Error::NoSpace) + } + + /// Find free space from back. + pub fn find_free_back( + &self, + len: Offset, + ) -> Result>, Error> { + if len.bytes() == 0 || len > self.addresses.end - self.addresses.start { + return Err(Error::InvalidRegion); + } + + if self.len == 0 { + return Ok(Span::new(self.addresses.end - len, len).into()); + } + + let mut next: Address = self.addresses.end; + for i in (0..self.len).rev() { + let prev = self.regions[i].addresses.end; + if len <= next - prev { + return Ok(Span::new(next - len, len).into()); } + next = self.regions[i].addresses.start; + } + + if len <= next - self.addresses.start { + return Ok(Span::new(next - len, len).into()); } - Err(Error::OutOfSpace) + Err(Error::NoSpace) } } @@ -217,9 +239,9 @@ mod tests { const B: Line> = Line::new(Address::new(0x3000), Address::new(0x5000)); let mut ledger: Ledger<8> = Ledger::new(LIMITS); - assert_eq!(ledger.find_free(D, true).unwrap(), A); + assert_eq!(ledger.find_free_front(D).unwrap(), A); ledger.insert(Region::new(A, 0)).unwrap(); - assert_eq!(ledger.find_free(D, true).unwrap(), B); + assert_eq!(ledger.find_free_front(D).unwrap(), B); } #[test] @@ -230,9 +252,9 @@ mod tests { const B: Line> = Line::new(Address::new(0xc000), Address::new(0xe000)); let mut ledger: Ledger<8> = Ledger::new(LIMITS); - assert_eq!(ledger.find_free(D, false).unwrap(), A); + assert_eq!(ledger.find_free_back(D).unwrap(), A); ledger.insert(Region::new(A, 0)).unwrap(); - assert_eq!(ledger.find_free(D, false).unwrap(), B); + assert_eq!(ledger.find_free_back(D).unwrap(), B); } #[test] From acd09b55c0ae0869c74635f37567cdbd29b2c4d5 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 24 Mar 2022 17:26:40 +0200 Subject: [PATCH 6/6] feat: `subtract()` Closes: #4 Signed-off-by: Jarkko Sakkinen --- src/lib.rs | 160 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 111 insertions(+), 49 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 73ed81d..ae8948b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ use core::cmp::Ordering; -use lset::{Empty, Line, Span}; +use lset::{Contains, Empty, Line, Span}; use primordial::{Address, Offset, Page}; /// A range of tagged address space in a ledger. @@ -43,14 +43,11 @@ impl Region { #[derive(Debug)] pub enum Error { /// Out of storage capacity - OutOfCapacity, + NoCapacity, /// No space for the region NoSpace, - /// Overlapping with the existing regions - Overlap, - /// Invalid region given as input InvalidRegion, } @@ -69,21 +66,6 @@ pub struct Ledger { } impl Ledger { - /// Sort the regions. - fn sort(&mut self) { - self.regions_mut().sort_unstable_by(|l, r| { - if l.addresses == r.addresses { - Ordering::Equal - } else if l.addresses.is_empty() { - Ordering::Greater - } else if r.addresses.is_empty() { - Ordering::Less - } else { - l.addresses.start.cmp(&r.addresses.start) - } - }) - } - /// Create a new instance. pub const fn new(addresses: Line>) -> Self { Self { @@ -99,13 +81,55 @@ impl Ledger { &self.regions[..self.len] } - /// Get a mutable view of the regions. - fn regions_mut(&mut self) -> &mut [Region] { - &mut self.regions[..self.len] + /// Delete a range of addresses from the regions. + pub fn delete(&mut self, addresses: Line>) -> Result<(), Error> { + if addresses.start >= addresses.end { + return Err(Error::InvalidRegion); + } + + if addresses.start < self.addresses.start || addresses.end > self.addresses.end { + return Err(Error::InvalidRegion); + } + + for i in 0..self.len { + // No contact: skip. + let shared = addresses.intersection(self.regions[i].addresses); + if shared.is_none() { + continue; + } + + let region_addresses = self.regions[i].addresses; + let value = self.regions[i].token; + + // Region fully covered: remove. + if addresses.contains(®ion_addresses) { + self.remove(i); + continue; + } + + // Piece fully covered: split the region and return. + if region_addresses.contains(&addresses) { + self.regions[i].addresses = Line::new(addresses.start, region_addresses.start); + return self.insert(Region::new( + Line::new(addresses.end, region_addresses.end), + value, + )); + } + + // Partially covered: adjust. + let shared = shared.unwrap(); + if shared.start > addresses.start { + self.regions[i].addresses = Line::new(addresses.start, shared.start); + } else { + self.regions[i].addresses = Line::new(shared.end, addresses.end); + } + } + + Ok(()) } - /// Insert a new region into the ledger. - pub fn insert(&mut self, region: Region) -> Result<(), Error> { + /// Merge a new region to the ledger. + pub fn merge(&mut self, region: Region) -> Result<(), Error> { if region.addresses.start >= region.addresses.end { return Err(Error::InvalidRegion); } @@ -117,17 +141,17 @@ impl Ledger { return Err(Error::InvalidRegion); } + if let Err(e) = self.delete(region.addresses) { + return Err(e); + } + // Loop over the regions looking for merges. let mut iter = self.regions_mut().iter_mut().peekable(); while let Some(prev) = iter.next() { - if prev.addresses.intersection(region.addresses).is_some() { - return Err(Error::Overlap); - } + assert!(prev.addresses.intersection(region.addresses).is_none()); if let Some(next) = iter.peek() { - if next.addresses.intersection(region.addresses).is_some() { - return Err(Error::Overlap); - } + assert!(next.addresses.intersection(region.addresses).is_none()); } // Merge previous. @@ -145,14 +169,7 @@ impl Ledger { } } - if self.len < self.regions.len() { - self.regions[self.len] = region; - self.len += 1; - self.sort(); - return Ok(()); - } - - Err(Error::OutOfCapacity) + self.insert(region) } /// Find free space from front. @@ -212,6 +229,51 @@ impl Ledger { Err(Error::NoSpace) } + + /// Get a mutable view of the regions. + fn regions_mut(&mut self) -> &mut [Region] { + &mut self.regions[..self.len] + } + + /// Insert a region. + fn insert(&mut self, region: Region) -> Result<(), Error> { + if self.len == self.regions.len() { + assert!(self.len <= self.regions.len()); + return Err(Error::NoCapacity); + } + + self.regions[self.len] = region; + self.len += 1; + self.sort(); + + Ok(()) + } + + /// Remove a region by index. + fn remove(&mut self, index: usize) { + assert!(self.len > 0); + assert!(index < self.len); + + self.regions[index] = self.regions[self.len - 1]; + self.regions[self.len - 1] = Region::empty(); // clear + self.len -= 1; + self.sort(); + } + + /// Sort the regions. + fn sort(&mut self) { + self.regions_mut().sort_unstable_by(|l, r| { + if l.addresses == r.addresses { + Ordering::Equal + } else if l.addresses.is_empty() { + Ordering::Greater + } else if r.addresses.is_empty() { + Ordering::Less + } else { + l.addresses.start.cmp(&r.addresses.start) + } + }) + } } #[cfg(test)] @@ -222,13 +284,13 @@ mod tests { Line::new(Address::new(0x1000), Address::new(0x10000)); #[test] - fn insert() { + fn merge() { const X: Line> = Line::new(Address::new(0xe000), Address::new(0x10000)); let mut ledger: Ledger<1> = Ledger::new(LIMITS); assert_eq!(ledger.len, 0); - ledger.insert(Region::new(X, 0)).unwrap(); + ledger.merge(Region::new(X, 0)).unwrap(); assert_eq!(ledger.regions(), &[Region::new(X, 0)]); } @@ -240,7 +302,7 @@ mod tests { let mut ledger: Ledger<8> = Ledger::new(LIMITS); assert_eq!(ledger.find_free_front(D).unwrap(), A); - ledger.insert(Region::new(A, 0)).unwrap(); + ledger.merge(Region::new(A, 0)).unwrap(); assert_eq!(ledger.find_free_front(D).unwrap(), B); } @@ -253,7 +315,7 @@ mod tests { let mut ledger: Ledger<8> = Ledger::new(LIMITS); assert_eq!(ledger.find_free_back(D).unwrap(), A); - ledger.insert(Region::new(A, 0)).unwrap(); + ledger.merge(Region::new(A, 0)).unwrap(); assert_eq!(ledger.find_free_back(D).unwrap(), B); } @@ -266,9 +328,9 @@ mod tests { const Y: Line> = Line::new(Address::new(0x4000), Address::new(0x6000)); let mut ledger: Ledger<8> = Ledger::new(LIMITS); - ledger.insert(Region::new(A, 0)).unwrap(); - ledger.insert(Region::new(B, 0)).unwrap(); - ledger.insert(Region::new(X, 0)).unwrap(); + ledger.merge(Region::new(A, 0)).unwrap(); + ledger.merge(Region::new(B, 0)).unwrap(); + ledger.merge(Region::new(X, 0)).unwrap(); assert_eq!(ledger.len, 2); assert_eq!(ledger.regions[0], Region::new(Y, 0)); @@ -284,9 +346,9 @@ mod tests { const Y: Line> = Line::new(Address::new(0x7000), Address::new(0x9000)); let mut ledger: Ledger<8> = Ledger::new(LIMITS); - ledger.insert(Region::new(A, 0)).unwrap(); - ledger.insert(Region::new(B, 0)).unwrap(); - ledger.insert(Region::new(X, 0)).unwrap(); + ledger.merge(Region::new(A, 0)).unwrap(); + ledger.merge(Region::new(B, 0)).unwrap(); + ledger.merge(Region::new(X, 0)).unwrap(); assert_eq!(ledger.len, 2); assert_eq!(ledger.regions[0].addresses, A);