Implementation Details

Relevant source files

This document provides detailed technical information about the internal implementation of the memory_set crate's core components. It covers the internal data structures, algorithms, and patterns used to implement memory mapping management functionality. For high-level concepts and architecture overview, see System Architecture. For usage examples and public API documentation, see Public API and Error Handling.

Core Data Structures and Types

The memory_set crate is built around three primary data structures that work together through a carefully designed generic type system.

MemoryArea Internal Structure

The MemoryArea struct serves as the fundamental building block representing a contiguous virtual memory region with uniform properties.

flowchart TD
subgraph subGraph3["Key Methods"]
    MAPAREA["map_area()"]
    UNMAPAREA["unmap_area()"]
    PROTECTAREA["protect_area()"]
    SPLIT["split()"]
    SHRINKLEFT["shrink_left()"]
    SHRINKRIGHT["shrink_right()"]
end
subgraph subGraph2["Type Constraints"]
    FCOPY["F: Copy"]
    BMAPPING["B: MappingBackend"]
    PGENERIC["P: Page Table Type"]
end
subgraph subGraph1["VirtAddrRange Components"]
    START["start: VirtAddr"]
    END["end: VirtAddr"]
end
subgraph subGraph0["MemoryArea Fields"]
    MA["MemoryArea"]
    VAR["va_range: VirtAddrRange"]
    FLAGS["flags: F"]
    BACKEND["backend: B"]
    PHANTOM["_phantom: PhantomData<(F,P)>"]
end

BACKEND --> BMAPPING
FLAGS --> FCOPY
MA --> BACKEND
MA --> FLAGS
MA --> MAPAREA
MA --> PHANTOM
MA --> PROTECTAREA
MA --> SHRINKLEFT
MA --> SHRINKRIGHT
MA --> SPLIT
MA --> UNMAPAREA
MA --> VAR
PHANTOM --> PGENERIC
VAR --> END
VAR --> START

The MemoryArea struct maintains both metadata about the virtual address range and a reference to the backend that handles the actual page table manipulation. The PhantomData field ensures proper generic type relationships without runtime overhead.

Sources: src/area.rs(L29 - L34)  src/area.rs(L36 - L76) 

MappingBackend Trait Implementation

The MappingBackend trait defines the interface for different memory mapping strategies, allowing the system to support various page table implementations and mapping policies.

flowchart TD
subgraph subGraph3["Implementation Requirements"]
    CLONE["Clone trait bound"]
    BOOLRESULT["Boolean success indicator"]
end
subgraph subGraph2["Generic Parameters"]
    F["F: Copy (Flags Type)"]
    P["P: Page Table Type"]
end
subgraph subGraph1["Backend Responsibilities"]
    MAPENTRY["Set page table entries"]
    CLEARENTRY["Clear page table entries"]
    UPDATEENTRY["Update entry permissions"]
end
subgraph subGraph0["MappingBackend Trait"]
    MB["MappingBackend"]
    MAP["map(start, size, flags, page_table) -> bool"]
    UNMAP["unmap(start, size, page_table) -> bool"]
    PROTECT["protect(start, size, new_flags, page_table) -> bool"]
end

F --> MB
MAP --> BOOLRESULT
MAP --> MAPENTRY
MB --> CLONE
MB --> MAP
MB --> PROTECT
MB --> UNMAP
P --> MB
PROTECT --> BOOLRESULT
PROTECT --> UPDATEENTRY
UNMAP --> BOOLRESULT
UNMAP --> CLEARENTRY

The trait's boolean return values allow backends to signal success or failure, which the higher-level operations convert into proper MappingResult types for error handling.

Sources: src/area.rs(L15 - L22)  src/area.rs(L90 - L110) 

MemorySet Collection Structure

The MemorySet uses a BTreeMap to maintain an ordered collection of memory areas, enabling efficient range queries and overlap detection.

flowchart TD
subgraph subGraph3["Core Methods"]
    MAPMETHOD["map()"]
    UNMAPMETHOD["unmap()"]
    PROTECTMETHOD["protect()"]
    OVERLAPS["overlaps()"]
    FIND["find()"]
    FINDFREE["find_free_area()"]
end
subgraph subGraph2["Efficiency Operations"]
    RANGEQUERIES["O(log n) range queries"]
    OVERLAP["Efficient overlap detection"]
    SEARCH["Binary search for areas"]
end
subgraph subGraph1["BTreeMap Key-Value Organization"]
    KEY1["Key: area.start()"]
    VAL1["Value: MemoryArea"]
    ORDERING["Sorted by start address"]
end
subgraph subGraph0["MemorySet Structure"]
    MS["MemorySet"]
    BTREE["areas: BTreeMap>"]
end

BTREE --> KEY1
BTREE --> VAL1
KEY1 --> ORDERING
MS --> BTREE
MS --> FIND
MS --> FINDFREE
MS --> MAPMETHOD
MS --> OVERLAPS
MS --> PROTECTMETHOD
MS --> UNMAPMETHOD
ORDERING --> OVERLAP
ORDERING --> RANGEQUERIES
ORDERING --> SEARCH

The choice of VirtAddr as the key ensures that areas are naturally sorted by their start addresses, which is crucial for the overlap detection and range manipulation algorithms.

Sources: src/set.rs(L9 - L11)  src/set.rs(L36 - L49) 

Memory Area Lifecycle Operations

Memory areas support sophisticated lifecycle operations that enable complex memory management patterns while maintaining consistency with the underlying page table.

Area Splitting Algorithm

The split() method implements a critical operation for handling partial unmapping and protection changes. It divides a single memory area into two independent areas at a specified boundary.

sequenceDiagram
    participant Client as Client
    participant MemoryArea as "MemoryArea"
    participant MappingBackend as "MappingBackend"

    Note over Client,MappingBackend: split(pos: VirtAddr) Operation
    Client ->> MemoryArea: split(pos)
    MemoryArea ->> MemoryArea: Validate pos in range (start < pos < end)
    alt Valid position
        MemoryArea ->> MemoryArea: Create new_area from pos to end
        Note over MemoryArea: new_area = MemoryArea::new(pos, end-pos, flags, backend.clone())
        MemoryArea ->> MemoryArea: Shrink original to start..pos
        Note over MemoryArea: self.va_range.end = pos
        MemoryArea ->> Client: Return Some(new_area)
    else Invalid position
        MemoryArea ->> Client: Return None
    end
    Note over MemoryArea,MappingBackend: No page table operations during split
    Note over MemoryArea: Backend cloned, both areas share same mapping behavior

The splitting operation is purely metadata manipulation - it doesn't modify the page table entries. The actual page table changes happen when subsequent operations like unmap() or protect() are called on the split areas.

Sources: src/area.rs(L148 - L163) 

Area Shrinking Operations

Memory areas support shrinking from either end, which is essential for handling partial unmapping operations efficiently.

flowchart TD
subgraph subGraph2["Error Handling"]
    CHECKRESULT["Check backend success"]
    SUCCESS["Update metadata"]
    FAILURE["Return MappingError::BadState"]
end
subgraph subGraph1["shrink_right() Process"]
    SR["shrink_right(new_size)"]
    CALCUNMAP2["unmap_size = old_size - new_size"]
    UNMAPRIGHT["backend.unmap(start + new_size, unmap_size)"]
    UPDATEEND["va_range.end -= unmap_size"]
end
subgraph subGraph0["shrink_left() Process"]
    SL["shrink_left(new_size)"]
    CALCUNMAP["unmap_size = old_size - new_size"]
    UNMAPLEFT["backend.unmap(start, unmap_size)"]
    UPDATESTART["va_range.start += unmap_size"]
end

CALCUNMAP --> UNMAPLEFT
CALCUNMAP2 --> UNMAPRIGHT
CHECKRESULT --> FAILURE
CHECKRESULT --> SUCCESS
SL --> CALCUNMAP
SR --> CALCUNMAP2
UNMAPLEFT --> CHECKRESULT
UNMAPLEFT --> UPDATESTART
UNMAPRIGHT --> CHECKRESULT
UNMAPRIGHT --> UPDATEEND

Both shrinking operations immediately update the page table through the backend before modifying the area's metadata, ensuring consistency between the virtual memory layout and page table state.

Sources: src/area.rs(L116 - L139) 

Collection Management Algorithms

The MemorySet implements sophisticated algorithms for managing collections of memory areas, particularly for handling overlapping operations and maintaining area integrity.

Overlap Detection Strategy

The overlap detection algorithm leverages the BTreeMap's ordered structure to efficiently check for conflicts with minimal tree traversal.

flowchart TD
subgraph subGraph0["BTreeMap Range Queries"]
    BEFORECHECK["Find last area before range.start"]
    AFTERCHECK["Find first area >= range.start"]
    RANGEBEFORE["areas.range(..range.start).last()"]
    RANGEAFTER["areas.range(range.start..).next()"]
end
START["overlaps(range: VirtAddrRange)"]
BEFOREOVERLAP["Does before area overlap with range?"]
RETURNTRUE["return true"]
AFTEROVERLAP["Does after area overlap with range?"]
RETURNFALSE["return false"]

AFTERCHECK --> AFTEROVERLAP
AFTERCHECK --> RANGEAFTER
AFTEROVERLAP --> RETURNFALSE
AFTEROVERLAP --> RETURNTRUE
BEFORECHECK --> BEFOREOVERLAP
BEFORECHECK --> RANGEBEFORE
BEFOREOVERLAP --> AFTERCHECK
BEFOREOVERLAP --> RETURNTRUE
START --> BEFORECHECK

This algorithm achieves O(log n) complexity by examining at most two areas, regardless of the total number of areas in the set.

Sources: src/set.rs(L36 - L49) 

Complex Unmapping Algorithm

The unmap() operation handles the most complex scenario in memory management: removing an arbitrary address range that may affect multiple existing areas in different ways.

flowchart TD
subgraph subGraph3["Phase 3 Details"]
    RIGHTRANGE["areas.range_mut(start..).next()"]
    RIGHTINTERSECT["Check if after.start < end"]
    RIGHTCASE["Shrink left of area"]
end
subgraph subGraph2["Phase 2 Details"]
    LEFTRANGE["areas.range_mut(..start).last()"]
    LEFTINTERSECT["Check if before.end > start"]
    LEFTCASE1["Case 1: Unmap at end of area"]
    LEFTCASE2["Case 2: Unmap in middle (split required)"]
end
subgraph subGraph1["Phase 1 Details"]
    RETAIN["areas.retain() with condition"]
    CONTAINED["area.va_range().contained_in(range)"]
    REMOVE["Remove and unmap area"]
end
subgraph subGraph0["unmap() Three-Phase Algorithm"]
    PHASE1["Phase 1: Remove Fully Contained Areas"]
    PHASE2["Phase 2: Handle Left Boundary Intersection"]
    PHASE3["Phase 3: Handle Right Boundary Intersection"]
end

CONTAINED --> REMOVE
LEFTINTERSECT --> LEFTCASE1
LEFTINTERSECT --> LEFTCASE2
LEFTRANGE --> LEFTINTERSECT
PHASE1 --> RETAIN
PHASE2 --> LEFTRANGE
PHASE3 --> RIGHTRANGE
RETAIN --> CONTAINED
RIGHTINTERSECT --> RIGHTCASE
RIGHTRANGE --> RIGHTINTERSECT

This three-phase approach ensures that all possible area-range relationships are handled correctly, from simple removal to complex splitting scenarios.

Sources: src/set.rs(L122 - L168) 

Protection Changes with Area Management

The protect() operation demonstrates the most sophisticated area manipulation, potentially creating new areas while modifying existing ones.

sequenceDiagram
    participant MemorySet as "MemorySet"
    participant BTreeMap as "BTreeMap"
    participant MemoryArea as "MemoryArea"
    participant MappingBackend as "MappingBackend"
    participant to_insertVec as "to_insert: Vec"

    Note over MemorySet,to_insertVec: protect(start, size, update_flags, page_table)
    MemorySet ->> BTreeMap: Iterate through all areas
    loop For each area in range
        BTreeMap ->> MemorySet: area reference
        MemorySet ->> MemorySet: Call update_flags(area.flags())
    alt Flags should be updated
        Note over MemorySet,to_insertVec: Determine area-range relationship
    alt Area fully contained in range
        MemorySet ->> MemoryArea: protect_area(new_flags)
        MemoryArea ->> MappingBackend: protect(start, size, new_flags)
        MemorySet ->> MemoryArea: set_flags(new_flags)
    else Range fully contained in area (split into 3)
        MemorySet ->> MemoryArea: split(end) -> right_part
        MemorySet ->> MemoryArea: set_end(start) (becomes left_part)
        MemorySet ->> MemorySet: Create middle_part with new_flags
        MemorySet ->> to_insertVec: Queue right_part and middle_part for insertion
    else Partial overlaps
        MemorySet ->> MemoryArea: split() and protect as needed
        MemorySet ->> to_insertVec: Queue new parts for insertion
    end
    end
    end
    MemorySet ->> BTreeMap: areas.extend(to_insert)

The algorithm defers insertions to avoid iterator invalidation, collecting new areas in a vector and inserting them after the main iteration completes.

Sources: src/set.rs(L189 - L247) 

Generic Type System Implementation

The crate's generic type system enables flexible memory management while maintaining type safety and zero-cost abstractions.

Type Parameter Relationships

flowchart TD
subgraph subGraph1["Type Flow Through System"]
    MAFLAG["MemoryArea.flags: F"]
    MBFLAG["MappingBackend.map(flags: F)"]
    MBPT["MappingBackend methods(page_table: &mut P)"]
    MABACKEND["MemoryArea.backend: B"]
    CLONE["Cloned for area.split()"]
end
subgraph subGraph0["Generic Type Constraints"]
    F["F: Copy + Debug"]
    P["P: Any"]
    B["B: MappingBackend + Clone"]
end
subgraph subGraph3["PhantomData Usage"]
    PHANTOM["PhantomData<(F,P)>"]
    TYPEREL["Maintains F,P relationship in MemoryArea"]
    ZEROSIZE["Zero runtime cost"]
    FCOPY["F: Copy enables efficient flag passing"]
    BCLONE["B: Clone enables area splitting"]
end
subgraph subGraph2["Trait Bounds Enforcement"]
    PHANTOM["PhantomData<(F,P)>"]
    FCOPY["F: Copy enables efficient flag passing"]
    BCLONE["B: Clone enables area splitting"]
    BMAPPING["B: MappingBackend ensures consistent interface"]
end

B --> CLONE
B --> MABACKEND
F --> MAFLAG
F --> MBFLAG
P --> MBPT
PHANTOM --> TYPEREL
PHANTOM --> ZEROSIZE

The PhantomData<(F,P)> field in MemoryArea ensures that the compiler tracks the relationship between flag type F and page table type P even though P is not directly stored in the struct.

Sources: src/area.rs(L29 - L34)  src/area.rs(L15 - L22) 

Error Handling Strategy

The crate implements a consistent error handling strategy that propagates failures through the operation chain while maintaining transactional semantics.

Error Propagation Pattern

flowchart TD
subgraph subGraph3["Error Handling Locations"]
    AREAOPS["MemoryArea operations"]
    SETOPS["MemorySet operations"]
    PUBLICAPI["Public API boundary"]
end
subgraph subGraph2["Error Propagation"]
    PROPAGATE["? operator propagation"]
end
subgraph subGraph1["Error Sources"]
    BACKEND["Backend operation failure"]
    VALIDATION["Parameter validation"]
    OVERLAP["Overlap detection"]
end
subgraph subGraph0["Error Type Hierarchy"]
    MR["MappingResult = Result"]
    ME["MappingError"]
    BE["BadState"]
    IE["InvalidParam"]
    AE["AlreadyExists"]
end

AE --> PROPAGATE
AREAOPS --> SETOPS
BACKEND --> BE
BE --> PROPAGATE
IE --> PROPAGATE
ME --> AE
ME --> BE
ME --> IE
OVERLAP --> AE
PROPAGATE --> AREAOPS
SETOPS --> PUBLICAPI
VALIDATION --> IE

The error handling design ensures that failures at any level (backend, area, or set) are properly propagated to the caller with meaningful error information.

Sources: src/area.rs(L6)  src/area.rs(L90 - L110)  src/set.rs(L98 - L114) 

Backend Error Translation

The system translates boolean failure indicators from backends into structured error types:

sequenceDiagram
    participant MemoryArea as "MemoryArea"
    participant MappingBackend as "MappingBackend"
    participant MappingResult as "MappingResult"

    Note over MemoryArea,MappingResult: Backend Error Translation Pattern
    MemoryArea ->> MappingBackend: map(start, size, flags, page_table)
    MappingBackend ->> MemoryArea: return bool (success/failure)
    alt Backend returns true
    MemoryArea ->> MappingResult: Ok(())
    else Backend returns false
    MemoryArea ->> MappingResult: Err(MappingError::BadState)
    end
    Note over MemoryArea,MappingResult: Pattern used in map_area(), unmap_area(), protect_area()

This translation happens in the MemoryArea methods that interface with the backend, converting the simple boolean results into the richer MappingResult type for higher-level error handling.

Sources: src/area.rs(L90 - L103)  src/area.rs(L116 - L139)