Skip to main content

os_essentials/
osalloc.rs

1#![no_std]
2
3use core::alloc::{GlobalAlloc, Layout};
4use core::ptr;
5use core::sync::atomic::{AtomicPtr, Ordering};
6use spin::Mutex;
7
8/// A struct representing a block of memory that is free and can be allocated.
9pub struct Node {
10    size: usize,                   // The size of the free memory block.
11    next: AtomicPtr<Node>,         // A pointer to the next free memory block in the free list.
12}
13
14/// A bump allocator with a free list to track freed memory blocks and avoid fragmentation.
15pub struct BumpAllocator {
16    start: usize,                  // The start address of the allocator's heap.
17    end: usize,                    // The end address of the allocator's heap.
18    current: usize,                // The current pointer for bump allocation.
19    free_list: AtomicPtr<Node>,    // The atomic pointer to the free list of memory blocks.
20    total_allocated: usize,        // The total memory allocated by the allocator.
21}
22
23impl BumpAllocator {
24    /// Creates a new instance of `BumpAllocator` with a given start and end address.
25    ///
26    /// # Parameters
27    /// - `start`: The start address of the heap.
28    /// - `end`: The end address of the heap.
29    ///
30    /// # Returns
31    /// A new `BumpAllocator` instance with the specified heap region.
32    pub const fn new(start: usize, end: usize) -> Self {
33        BumpAllocator {
34            start,
35            end,
36            current: start,
37            free_list: AtomicPtr::new(ptr::null_mut()),
38            total_allocated: 0,
39        }
40    }
41
42    /// Allocates memory of the specified layout from the heap.
43    ///
44    /// # Safety
45    /// This function is unsafe because it involves directly manipulating raw pointers.
46    ///
47    /// # Parameters
48    /// - `layout`: The memory layout that specifies the size and alignment of the allocation.
49    ///
50    /// # Returns
51    /// A pointer to the allocated memory, or `null_mut` if allocation fails.
52    pub unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
53        let size = layout.size();
54        
55        // Check if there are any free blocks in the free list.
56        if !self.free_list.load(Ordering::SeqCst).is_null() {
57            let mut current_node = self.free_list.load(Ordering::SeqCst);
58
59            // Traverse the free list to find a block that fits the requested size.
60            while !current_node.is_null() {
61                let node_size = (*current_node).size;
62
63                if node_size >= size {
64                    // If a suitable block is found, remove it from the free list.
65                    self.free_list.store((*current_node).next.load(Ordering::SeqCst), Ordering::SeqCst);
66                    self.total_allocated += size;
67                    return current_node as *mut u8; // Return the pointer to the allocated block.
68                }
69
70                current_node = (*current_node).next.load(Ordering::SeqCst);
71            }
72        }
73
74        // No suitable block found; fallback to bump allocation.
75        let new_start = self.current;
76        let new_end = new_start + size;
77
78        // If there is enough space in the heap, allocate memory by bumping the current pointer.
79        if new_end <= self.end {
80            self.current = new_end;
81            self.total_allocated += size;
82            new_start as *mut u8
83        } else {
84            ptr::null_mut() // Return null if there is not enough space left.
85        }
86    }
87
88    /// Deallocates a previously allocated block of memory and adds it to the free list.
89    ///
90    /// # Safety
91    /// This function is unsafe because it involves directly manipulating raw pointers.
92    ///
93    /// # Parameters
94    /// - `ptr`: A pointer to the memory block to be deallocated.
95    /// - `layout`: The layout of the memory block to be deallocated.
96    pub unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
97        let size = layout.size();
98        let new_node = ptr as *mut Node;
99
100        // Add the deallocated block to the free list.
101        (*new_node) = Node {
102            size,
103            next: AtomicPtr::new(self.free_list.load(Ordering::SeqCst)),
104        };
105
106        // Update the free list to point to the new node.
107        self.free_list.store(new_node, Ordering::SeqCst);
108    }
109
110    /// Returns the amount of available memory in the heap.
111    ///
112    /// # Returns
113    /// The number of bytes of available memory.
114    pub fn available_memory(&self) -> usize {
115        self.end - self.current
116    }
117
118    /// Returns the total size of the heap.
119    ///
120    /// # Returns
121    /// The size of the heap in bytes.
122    pub fn heap_size(&self) -> usize {
123        self.end - self.start
124    }
125
126    /// Returns the total memory allocated by the allocator.
127    ///
128    /// # Returns
129    /// The total memory allocated in bytes.
130    pub fn used_memory(&self) -> usize {
131        self.total_allocated
132    }
133
134    /// Returns the amount of remaining memory in the heap.
135    ///
136    /// # Returns
137    /// The number of bytes of remaining memory.
138    pub fn remaining_memory(&self) -> usize {
139        self.heap_size() - self.total_allocated
140    }
141}
142
143/// A global allocator instance that uses the bump allocator to allocate and deallocate memory.
144pub static GLOBAL_ALLOCATOR: Mutex<BumpAllocator> = Mutex::new(BumpAllocator::new(0x90000, 0xA0000));
145
146/// A wrapper for the global allocator that implements the `GlobalAlloc` trait.
147pub struct GlobalAllocatorWrapper;
148
149unsafe impl GlobalAlloc for GlobalAllocatorWrapper {
150    /// Allocates memory from the global bump allocator.
151    ///
152    /// # Safety
153    /// This function is unsafe because it involves raw pointer manipulation.
154    ///
155    /// # Parameters
156    /// - `layout`: The layout of the memory to be allocated.
157    ///
158    /// # Returns
159    /// A pointer to the allocated memory.
160    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
161        let mut allocator = GLOBAL_ALLOCATOR.lock();
162        allocator.alloc(layout)
163    }
164
165    /// Deallocates a previously allocated block of memory in the global allocator.
166    ///
167    /// # Safety
168    /// This function is unsafe because it involves raw pointer manipulation.
169    ///
170    /// # Parameters
171    /// - `ptr`: A pointer to the memory to be deallocated.
172    /// - `layout`: The layout of the memory to be deallocated.
173    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
174        let mut allocator = GLOBAL_ALLOCATOR.lock();
175        allocator.dealloc(ptr, layout);
176    }
177}