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}