MemorySet Collection Management
Relevant source files
This document covers the internal collection management mechanisms of MemorySet
, focusing on how it organizes, tracks, and manipulates collections of memory areas using a BTreeMap
data structure. It examines overlap detection algorithms, area lifecycle operations, and complex manipulation procedures like splitting and shrinking.
For information about individual MemoryArea
objects and the MappingBackend
trait, see MemoryArea and MappingBackend. For the public API interface, see Public API and Error Handling.
Core Data Structure Organization
The MemorySet
struct uses a BTreeMap<VirtAddr, MemoryArea<F, P, B>>
as its primary storage mechanism, where the key is the starting virtual address of each memory area. This design provides efficient logarithmic-time operations for address-based queries and maintains areas in sorted order by their start addresses.
MemorySet BTreeMap Organization
flowchart TD subgraph subGraph2["Key Properties"] Sorted["Keys sorted by address"] Unique["Each key is area.start()"] NoOverlap["No overlapping ranges"] end subgraph subGraph1["BTreeMap Structure"] Key1["va!(0x1000)"] Key2["va!(0x3000)"] Key3["va!(0x5000)"] Area1["MemoryArea[0x1000-0x2000]"] Area2["MemoryArea[0x3000-0x4000]"] Area3["MemoryArea[0x5000-0x6000]"] end subgraph MemorySet["MemorySet<F,P,B>"] BTree["BTreeMap<VirtAddr, MemoryArea>"] end BTree --> Key1 BTree --> Key2 BTree --> Key3 BTree --> NoOverlap BTree --> Sorted BTree --> Unique Key1 --> Area1 Key2 --> Area2 Key3 --> Area3
Sources: src/set.rs(L9 - L11)
Basic Collection Operations
The MemorySet
provides fundamental collection operations that leverage the BTreeMap
's sorted structure:
Operation | Method | Time Complexity | Purpose |
---|---|---|---|
Count areas | len() | O(1) | Returns number of areas |
Check emptiness | is_empty() | O(1) | Tests if collection is empty |
Iterate areas | iter() | O(n) | Provides iterator over all areas |
Find by address | find(addr) | O(log n) | Locates area containing address |
The find()
method demonstrates efficient address lookup using range queries on the BTreeMap
:
Address Lookup Algorithm
flowchart TD Start["find(addr)"] RangeQuery["areas.range(..=addr).last()"] GetCandidate["Get last area with start ≤ addr"] CheckContains["Check if area.va_range().contains(addr)"] ReturnArea["Return Some(area)"] ReturnNone["Return None"] CheckContains --> ReturnArea CheckContains --> ReturnNone GetCandidate --> CheckContains RangeQuery --> GetCandidate Start --> RangeQuery
Sources: src/set.rs(L21 - L55)
Overlap Detection and Management
The overlaps()
method implements efficient overlap detection by checking at most two adjacent areas in the sorted collection, rather than scanning all areas:
Overlap Detection Strategy
flowchart TD CheckOverlap["overlaps(range)"] CheckBefore["Check area before range.start"] CheckAfter["Check area at/after range.start"] BeforeRange["areas.range(..range.start).last()"] AfterRange["areas.range(range.start..).next()"] TestBeforeOverlap["before.va_range().overlaps(range)"] TestAfterOverlap["after.va_range().overlaps(range)"] ReturnTrue1["Return true"] ReturnTrue2["Return true"] ContinueAfter["Continue to after check"] ReturnFalse["Return false"] AfterRange --> TestAfterOverlap BeforeRange --> TestBeforeOverlap CheckAfter --> AfterRange CheckBefore --> BeforeRange CheckOverlap --> CheckAfter CheckOverlap --> CheckBefore TestAfterOverlap --> ReturnFalse TestAfterOverlap --> ReturnTrue2 TestBeforeOverlap --> ContinueAfter TestBeforeOverlap --> ReturnTrue1
This algorithm achieves O(log n) complexity by exploiting the sorted nature of the BTreeMap
and the non-overlapping invariant of stored areas.
Sources: src/set.rs(L36 - L49)
Memory Area Addition and Conflict Resolution
The map()
operation handles the complex process of adding new areas while managing potential conflicts:
Map Operation Flow
sequenceDiagram participant Client as Client participant MemorySet as MemorySet participant BTreeMap as BTreeMap participant MemoryArea as MemoryArea Client ->> MemorySet: "map(area, page_table, unmap_overlap)" MemorySet ->> MemorySet: "Validate area.va_range().is_empty()" MemorySet ->> MemorySet: "overlaps(area.va_range())" alt "Overlap detected" alt "unmap_overlap = true" MemorySet ->> MemorySet: "unmap(area.start(), area.size(), page_table)" Note over MemorySet: "Remove conflicting areas" else "unmap_overlap = false" MemorySet -->> Client: "MappingError::AlreadyExists" end end MemorySet ->> MemoryArea: "area.map_area(page_table)" MemoryArea -->> MemorySet: "MappingResult" MemorySet ->> BTreeMap: "insert(area.start(), area)" MemorySet -->> Client: "Success"
Sources: src/set.rs(L85 - L114)
Complex Area Manipulation Operations
Unmap Operation with Area Splitting
The unmap()
operation implements sophisticated area manipulation, including splitting areas when the unmapped region falls in the middle of an existing area:
Unmap Cases and Transformations
flowchart TD subgraph subGraph3["Case 4: Middle Split"] subgraph subGraph1["Case 2: Right Boundary Intersection"] C4Before["[████████████████][unmap]"] C4After1["[██] [██]split into two"] C3Before["[████████████][unmap]"] C3After1["[████]shrink_left"] C2Before["[██████████████][unmap]"] C2After1["[████]shrink_right"] C1Before["[████████████]Existing Area"] C1After1["(removed)"] subgraph subGraph2["Case 3: Left Boundary Intersection"] subgraph subGraph0["Case 1: Full Containment"] C4Before["[████████████████][unmap]"] C4After1["[██] [██]split into two"] C3Before["[████████████][unmap]"] C3After1["[████]shrink_left"] C2Before["[██████████████][unmap]"] C2After1["[████]shrink_right"] C1Before["[████████████]Existing Area"] C1After1["(removed)"] end end end end C1Before --> C1After1 C2Before --> C2After1 C3Before --> C3After1 C4Before --> C4After1
The implementation handles these cases through a three-phase process:
- Full Removal: Remove areas completely contained in the unmap range using
retain()
- Left Boundary Processing: Shrink or split areas that intersect the left boundary
- Right Boundary Processing: Shrink areas that intersect the right boundary
Sources: src/set.rs(L116 - L169)
Protect Operation with Multi-way Splitting
The protect()
operation changes memory flags within a range and may require splitting existing areas into multiple parts:
Protect Operation Area Handling
Sources: src/set.rs(L180 - L247)
Free Space Management
The find_free_area()
method locates unallocated address ranges by examining gaps between consecutive areas:
Free Area Search Algorithm
flowchart TD FindFree["find_free_area(hint, size, limit)"] InitLastEnd["last_end = max(hint, limit.start)"] IterateAreas["For each (addr, area) in areas"] CheckGap["if last_end + size <= addr"] ReturnGap["Return Some(last_end)"] UpdateEnd["last_end = area.end()"] NextArea["Continue to next area"] NoMoreAreas["End of areas reached"] CheckFinalGap["if last_end + size <= limit.end"] ReturnFinal["Return Some(last_end)"] ReturnNone["Return None"] CheckFinalGap --> ReturnFinal CheckFinalGap --> ReturnNone CheckGap --> ReturnGap CheckGap --> UpdateEnd FindFree --> InitLastEnd InitLastEnd --> IterateAreas IterateAreas --> CheckGap IterateAreas --> NoMoreAreas NextArea --> IterateAreas NoMoreAreas --> CheckFinalGap UpdateEnd --> NextArea
Sources: src/set.rs(L57 - L83)
Collection Cleanup Operations
The clear()
method provides atomic cleanup of all memory areas and their underlying mappings:
// Unmaps all areas from the page table, then clears the collection
pub fn clear(&mut self, page_table: &mut P) -> MappingResult
This operation iterates through all areas, calls unmap_area()
on each, and then clears the BTreeMap
if all unmapping operations succeed.
Sources: src/set.rs(L171 - L178)