memory/manager.rs
1use core::{
2 alloc::GlobalAlloc,
3 ptr::{NonNull, null_mut},
4};
5
6use crate::{CapabilityFlags, Layout, ManagerTrait};
7
8unsafe extern "Rust" {
9 unsafe static __XILA_MEMORY_MANAGER: Manager<'static>;
10}
11
12/// A wrapper type that adapts any type implementing `Allocator_trait` to the standard
13/// Rust `GlobalAlloc` trait.
14///
15/// This enables custom allocators that implement the Xila-specific `Allocator_trait`
16/// to be used as the global memory allocator for Rust's allocation system.
17pub struct Manager<'a>(&'a dyn ManagerTrait);
18
19impl<'a> Manager<'a> {
20 /// Creates a new instance of `Allocator_type` wrapping the provided allocator.
21 ///
22 /// # Parameters
23 /// * `Allocator` - The allocator to wrap
24 ///
25 /// # Returns
26 /// A new instance of `Allocator_type` containing the provided allocator.
27 pub const fn new(allocator: &'a dyn ManagerTrait) -> Self {
28 Self(allocator)
29 }
30
31 /// Allocates memory with the specified capabilities and layout.
32 ///
33 /// # Parameters
34 /// * `Capabilities` - Specific requirements for the allocation
35 /// * `Layout` - Size and alignment requirements for the allocation
36 ///
37 /// # Returns
38 /// A pointer to the allocated memory, or a null pointer if allocation failed.
39 ///
40 /// # Safety
41 /// This function is unsafe because the caller must ensure that:
42 pub unsafe fn allocate(&self, capabilities: CapabilityFlags, layout: Layout) -> *mut u8 {
43 if layout.size() == 0 {
44 return null_mut();
45 }
46
47 match unsafe { self.0.allocate(capabilities, layout) } {
48 Some(ptr) => ptr.as_ptr(),
49 None => {
50 null_mut()
51 // panic!(
52 // "xila_memory_allocate for capabilities {:?} and layout {:?} failed",
53 // capabilities, layout
54 // );
55 }
56 }
57 }
58
59 /// Deallocates memory previously allocated by this allocator.
60 ///
61 /// # Parameters
62 /// * `Pointer` - Pointer to the memory to deallocate
63 /// * `Layout` - The layout that was used to allocate the memory
64 ///
65 /// # Safety
66 /// This function is unsafe because the caller must ensure that:
67 /// - The pointer was allocated by this allocator
68 /// - The layout matches the layout used for allocation
69 pub unsafe fn deallocate(&self, pointer: *mut u8, layout: Layout) {
70 if let Some(pointer) = NonNull::new(pointer) {
71 unsafe { self.0.deallocate(pointer, layout) }
72 } else {
73 // No panic since its allowed in C
74 // panic!("Attempted to deallocate a null pointer");
75 }
76 }
77
78 /// Reallocates memory with a new layout.
79 ///
80 /// This method changes the size or alignment of a previously allocated memory block.
81 /// If the pointer is `None`, this behaves like a normal allocation.
82 /// If reallocation is successful, the contents of the old memory are preserved
83 /// up to the minimum of the old and new sizes.
84 ///
85 /// # Parameters
86 /// * `Pointer` - Pointer to the memory to reallocate. If null, acts as a new allocation
87 /// * `Old_layout` - The layout that was used for the original allocation
88 /// * `New_size` - The new size for the memory
89 ///
90 /// # Returns
91 /// A pointer to the reallocated memory with the new layout, or a null pointer if reallocation failed.
92 ///
93 /// # Safety
94 /// This function is unsafe because the caller must ensure that:
95 /// - If `Pointer` is not null, it was allocated by this allocator
96 /// - The `Old_layout` matches the one used for the original allocation
97 /// - The old memory is not used after successful reallocation
98 /// - The returned memory is properly initialized before use
99 pub unsafe fn reallocate(&self, pointer: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
100 let new_layout = Layout::from_size_align(new_size, layout.align()).unwrap_or(layout);
101
102 let pointer = match NonNull::new(pointer) {
103 Some(pointer) => unsafe { self.0.reallocate(pointer, layout, new_layout) },
104 None => unsafe { self.0.allocate(CapabilityFlags::None, new_layout) },
105 };
106
107 pointer.map_or(null_mut(), |ptr| ptr.as_ptr())
108 }
109
110 pub fn get_page_size(&self) -> usize {
111 self.0.get_page_size()
112 }
113
114 pub fn flush_data_cache(&self) {
115 self.0.flush_data_cache()
116 }
117
118 /// Flushes the instruction cache for the specified memory region.
119 ///
120 /// # Parameters
121 /// * `Address` - Starting address of the memory region
122 /// * `Size` - Size of the memory region in bytes
123 /// # Safety
124 /// This function is unsafe because the caller must ensure that:
125 /// - The address is valid and points to a memory region of at least `size` bytes
126 /// - The memory region is not being modified while the instruction cache is being flushed
127 pub unsafe fn flush_instruction_cache(&self, address: *const u8, size: usize) {
128 let address = if let Some(address) = NonNull::new(address as *mut u8) {
129 address
130 } else {
131 log::warning!("flush_instruction_cache called with null address, ignoring");
132 return;
133 };
134
135 self.0.flush_instruction_cache(address, size)
136 }
137
138 /// Returns the amount of memory currently used.
139 ///
140 /// # Returns
141 /// The number of bytes currently allocated.
142 pub fn get_used(&self) -> usize {
143 self.0.get_used()
144 }
145
146 /// Returns the amount of memory currently available.
147 ///
148 /// # Returns
149 /// The number of bytes available for allocation.
150 ///
151 pub fn get_free(&self) -> usize {
152 self.0.get_free()
153 }
154
155 /// Returns the total size of the memory managed by this allocator.
156 ///
157 /// # Returns
158 /// The total number of bytes managed by the allocator.
159 pub fn get_total_size(&self) -> usize {
160 self.0.get_total_size()
161 }
162}
163
164/// Implementation of the standard library's `GlobalAlloc` trait for any wrapped
165/// type that implements `Allocator_trait`.
166///
167/// This implementation delegates the standard allocation operations to the wrapped
168/// allocator's methods, converting between Rust's and Xila's allocation types.
169///
170/// # Safety
171/// The implementation upholds the safety guarantees required by `GlobalAlloc`:
172/// - Memory is properly aligned according to the layout
173/// - Deallocation uses the same layout that was used for allocation
174unsafe impl<'a> GlobalAlloc for Manager<'a> {
175 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
176 unsafe { self.allocate(CapabilityFlags::None, layout) }
177 }
178
179 unsafe fn dealloc(&self, pointer: *mut u8, layout: Layout) {
180 unsafe { self.deallocate(pointer, layout) }
181 }
182
183 unsafe fn realloc(&self, pointer: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
184 unsafe { self.reallocate(pointer, layout, new_size) }
185 }
186}
187
188pub fn get_instance() -> &'static Manager<'static> {
189 unsafe { &__XILA_MEMORY_MANAGER }
190}