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}