Overview
Relevant source files
This document provides a comprehensive overview of the slab_allocator crate, a hybrid memory allocation system designed for no_std environments. The allocator combines the performance benefits of slab allocation for small, fixed-size blocks with the flexibility of buddy allocation for larger, variable-size requests.
For detailed implementation specifics of individual components, see Core Architecture. For practical usage examples and setup instructions, see Getting Started.
Purpose and Target Environment
The slab_allocator crate addresses the critical need for efficient memory management in resource-constrained environments where the standard library is unavailable. It targets embedded systems, kernel development, and real-time applications that require deterministic allocation performance.
The allocator implements a hybrid strategy that provides O(1) allocation for blocks ≤ 4096 bytes through slab allocation, while falling back to a buddy system allocator for larger requests. This design balances performance predictability with memory utilization efficiency.
Sources: Cargo.toml(L8 - L9) src/lib.rs(L1 - L3)
System Architecture
The allocator's architecture centers around the Heap struct, which orchestrates allocation requests between multiple specialized components based on request size and alignment requirements.
Core Components Mapping
flowchart TD
subgraph subGraph4["Internal Slab Components"]
FreeBlockList["FreeBlockList<BLK_SIZE>(src/slab.rs:65)"]
FreeBlock["FreeBlock(src/slab.rs:112)"]
end
subgraph subGraph3["Large Block Allocation"]
BuddyAllocator["buddy_allocatorbuddy_system_allocator::Heap<32>(src/lib.rs:48)"]
end
subgraph subGraph2["Slab Allocation System"]
Slab64["slab_64_bytes: Slab<64>(src/lib.rs:41)"]
Slab128["slab_128_bytes: Slab<128>(src/lib.rs:42)"]
Slab256["slab_256_bytes: Slab<256>(src/lib.rs:43)"]
Slab512["slab_512_bytes: Slab<512>(src/lib.rs:44)"]
Slab1024["slab_1024_bytes: Slab<1024>(src/lib.rs:45)"]
Slab2048["slab_2048_bytes: Slab<2048>(src/lib.rs:46)"]
Slab4096["slab_4096_bytes: Slab<4096>(src/lib.rs:47)"]
end
subgraph subGraph1["Allocation Routing"]
HeapAllocator["HeapAllocator enum(src/lib.rs:27)"]
layout_to_allocator["layout_to_allocator()(src/lib.rs:208)"]
end
subgraph subGraph0["Primary Interface"]
Heap["Heap(src/lib.rs:40)"]
end
FreeBlockList --> FreeBlock
Heap --> HeapAllocator
Heap --> layout_to_allocator
Slab1024 --> FreeBlockList
Slab128 --> FreeBlockList
Slab2048 --> FreeBlockList
Slab256 --> FreeBlockList
Slab4096 --> FreeBlockList
Slab512 --> FreeBlockList
Slab64 --> FreeBlockList
layout_to_allocator --> BuddyAllocator
layout_to_allocator --> Slab1024
layout_to_allocator --> Slab128
layout_to_allocator --> Slab2048
layout_to_allocator --> Slab256
layout_to_allocator --> Slab4096
layout_to_allocator --> Slab512
layout_to_allocator --> Slab64
Sources: src/lib.rs(L40 - L49) src/lib.rs(L27 - L36) src/slab.rs(L4 - L7) src/slab.rs(L65 - L68)
Allocation Decision Flow
The system routes allocation requests through a deterministic decision tree based on the Layout parameter's size and alignment requirements.
flowchart TD Request["allocate(layout: Layout)(src/lib.rs:135)"] Router["layout_to_allocator(&layout)(src/lib.rs:208-226)"] SizeCheck["Size > 4096?(src/lib.rs:209)"] Buddy["HeapAllocator::BuddyAllocator(src/lib.rs:210)"] AlignCheck["Check size and alignmentconstraints (src/lib.rs:211-225)"] Slab64Check["≤64 && align≤64?(src/lib.rs:211-212)"] Slab128Check["≤128 && align≤128?(src/lib.rs:213-214)"] Slab256Check["≤256 && align≤256?(src/lib.rs:215-216)"] Slab512Check["≤512 && align≤512?(src/lib.rs:217-218)"] Slab1024Check["≤1024 && align≤1024?(src/lib.rs:219-220)"] Slab2048Check["≤2048 && align≤2048?(src/lib.rs:221-222)"] SlabDefault["HeapAllocator::Slab4096Bytes(src/lib.rs:224)"] Slab64Enum["HeapAllocator::Slab64Bytes"] Slab128Enum["HeapAllocator::Slab128Bytes"] Slab256Enum["HeapAllocator::Slab256Bytes"] Slab512Enum["HeapAllocator::Slab512Bytes"] Slab1024Enum["HeapAllocator::Slab1024Bytes"] Slab2048Enum["HeapAllocator::Slab2048Bytes"] BuddyImpl["buddy_allocator.alloc()(src/lib.rs:158-162)"] SlabImpl["slab_X_bytes.allocate()(src/lib.rs:137-157)"] AlignCheck --> Slab1024Check AlignCheck --> Slab128Check AlignCheck --> Slab2048Check AlignCheck --> Slab256Check AlignCheck --> Slab512Check AlignCheck --> Slab64Check AlignCheck --> SlabDefault Buddy --> BuddyImpl Request --> Router Router --> SizeCheck SizeCheck --> AlignCheck SizeCheck --> Buddy Slab1024Check --> Slab1024Enum Slab1024Enum --> SlabImpl Slab128Check --> Slab128Enum Slab128Enum --> SlabImpl Slab2048Check --> Slab2048Enum Slab2048Enum --> SlabImpl Slab256Check --> Slab256Enum Slab256Enum --> SlabImpl Slab512Check --> Slab512Enum Slab512Enum --> SlabImpl Slab64Check --> Slab64Enum Slab64Enum --> SlabImpl SlabDefault --> SlabImpl
Sources: src/lib.rs(L135 - L164) src/lib.rs(L208 - L226)
Memory Management Strategy
The allocator employs a two-tier memory management approach that optimizes for different allocation patterns:
| Allocation Size | Strategy | Allocator | Time Complexity | Use Case |
|---|---|---|---|---|
| ≤ 64 bytes | Fixed-size slab | Slab<64> | O(1) | Small objects, metadata |
| 65-128 bytes | Fixed-size slab | Slab<128> | O(1) | Small structures |
| 129-256 bytes | Fixed-size slab | Slab<256> | O(1) | Medium structures |
| 257-512 bytes | Fixed-size slab | Slab<512> | O(1) | Small buffers |
| 513-1024 bytes | Fixed-size slab | Slab<1024> | O(1) | Medium buffers |
| 1025-2048 bytes | Fixed-size slab | Slab<2048> | O(1) | Large structures |
| 2049-4096 bytes | Fixed-size slab | Slab<4096> | O(1) | Page-sized allocations |
| > 4096 bytes | Buddy system | buddy_system_allocator | O(log n) | Large buffers, dynamic data |
Sources: src/lib.rs(L208 - L226) src/lib.rs(L41 - L48)
Dynamic Growth Mechanism
When a slab exhausts its available blocks, the allocator automatically requests additional memory from the buddy allocator in fixed-size chunks defined by SET_SIZE (64 blocks per growth operation).
flowchart TD SlabEmpty["Slab free_block_list empty(src/slab.rs:42)"] RequestGrowth["Request SET_SIZE * BLK_SIZEfrom buddy allocator(src/slab.rs:43-47)"] GrowSlab["grow() creates new FreeBlockList(src/slab.rs:26-33)"] MergeBlocks["Merge new blocks intoexisting free_block_list(src/slab.rs:30-32)"] ReturnBlock["Return first available block(src/slab.rs:49)"] GrowSlab --> MergeBlocks MergeBlocks --> ReturnBlock RequestGrowth --> GrowSlab SlabEmpty --> RequestGrowth
Sources: src/slab.rs(L35 - L55) src/slab.rs(L26 - L33) src/lib.rs(L24)
Platform Support and Testing
The allocator supports multiple embedded architectures and maintains compatibility through extensive CI/CD testing:
| Target Platform | Purpose | Test Coverage |
|---|---|---|
| x86_64-unknown-linux-gnu | Development/testing | Full unit tests |
| x86_64-unknown-none | Bare metal x86_64 | Build verification |
| riscv64gc-unknown-none-elf | RISC-V embedded | Build verification |
| aarch64-unknown-none-softfloat | ARM64 embedded | Build verification |
The test suite includes allocation patterns from simple double-usize allocations to complex multi-size scenarios with varying alignment requirements, ensuring robust behavior across different usage patterns.
Sources: .github/workflows/ci.yml(L12) .github/workflows/ci.yml(L28 - L30) src/tests.rs(L39 - L163)
Key Features and Constraints
Design Features
no_stdcompatibility: No dependency on the standard library- Deterministic performance: O(1) allocation for blocks ≤ 4096 bytes
- Automatic growth: Dynamic expansion when slabs become exhausted
- Memory statistics: Runtime visibility into allocation patterns
- Multiple alignment support: Handles various alignment requirements efficiently
System Constraints
- Minimum heap size: 32KB (
MIN_HEAP_SIZE = 0x8000) - Page alignment requirement: Heap start address must be 4096-byte aligned
- Growth granularity: Memory added in 4096-byte increments
- Slab threshold: Fixed 4096-byte boundary between slab and buddy allocation
Sources: src/lib.rs(L25) src/lib.rs(L59 - L71) src/lib.rs(L95 - L106)