memory/
trait.rs

1use core::ptr::NonNull;
2
3use crate::{Capabilities, Layout};
4
5/// Trait that defines the interface for memory allocators in the Xila system.
6///
7/// Implementors of this trait can be used to manage memory allocation and deallocation
8/// operations with specific capabilities. The trait provides the foundation for
9/// custom memory allocation strategies.
10///
11/// # Safety
12///
13/// All methods in this trait are unsafe because they deal with raw memory and can
14/// cause undefined behavior if used incorrectly, such as:
15/// - Deallocating memory that wasn't allocated with the same allocator
16/// - Using memory after it has been deallocated
17/// - Deallocating the same memory multiple times
18pub trait ManagerTrait: Send + Sync {
19    /// Allocates memory with the specified capabilities and layout.
20    ///
21    /// # Parameters
22    /// * `Capabilities` - Specific requirements for the allocation
23    /// * `Layout` - Size and alignment requirements for the allocation
24    ///
25    /// # Returns
26    /// A pointer to the allocated memory, where the protection is set to [`crate::Protection::READ`], or a null pointer if allocation failed.
27    ///
28    /// # Safety
29    /// This function is unsafe because the caller must ensure that:
30    /// - The returned memory is properly initialized before use
31    /// - The memory is properly deallocated when no longer needed
32    unsafe fn allocate(&self, capabilities: Capabilities, layout: Layout) -> Option<NonNull<u8>>;
33
34    /// Deallocates memory previously allocated by this allocator.
35    ///
36    /// # Parameters
37    /// * `Pointer` - Pointer to the memory to deallocate
38    /// * `Layout` - The layout that was used to allocate the memory
39    ///
40    /// # Safety
41    /// This function is unsafe because the caller must ensure that:
42    /// - The pointer was allocated by this allocator
43    /// - The layout matches the one used for allocation
44    /// - The memory is not used after deallocation
45    /// - The memory is not deallocated multiple times
46    unsafe fn deallocate(&self, pointer: NonNull<u8>, layout: Layout);
47
48    /// Reallocates memory with a new layout.
49    ///
50    /// This method changes the size or alignment of a previously allocated memory block.
51    /// If the pointer is `None`, this behaves like a normal allocation.
52    /// If reallocation is successful, the contents of the old memory are preserved
53    /// up to the minimum of the old and new sizes.
54    ///
55    /// # Parameters
56    /// * `Pointer` - Optional pointer to the memory to reallocate. If `None`, acts as a new allocation
57    /// * `Old_layout` - The layout that was used for the original allocation
58    /// * `New_layout` - The new layout requirements for the memory
59    ///
60    /// # Returns
61    /// A pointer to the reallocated memory with the new layout, or `None` if reallocation failed.
62    /// The protection is set to [`crate::Protection::READ`].
63    ///
64    /// # Safety
65    /// This function is unsafe because the caller must ensure that:
66    /// - If `Pointer` is `Some`, it was allocated by this allocator
67    /// - The `Old_layout` matches the one used for the original allocation
68    /// - The old memory is not used after successful reallocation
69    /// - The returned memory is properly initialized before use
70    unsafe fn reallocate(
71        &self,
72        pointer: Option<NonNull<u8>>,
73        old_layout: Layout,
74        new_layout: Layout,
75    ) -> Option<NonNull<u8>> {
76        // Default implementation simply deallocates and allocates again
77        let memory = unsafe { self.allocate(Capabilities::default(), new_layout) }?;
78
79        // Copy the old data to the new memory
80        let pointer = match pointer {
81            Some(ptr) => ptr,
82            None => return Some(memory),
83        };
84
85        let old_size = old_layout.size();
86        let new_size = new_layout.size();
87        if old_size > 0 && new_size > 0 {
88            let old_ptr = pointer.as_ptr();
89            let new_ptr = memory.as_ptr();
90            unsafe {
91                core::ptr::copy_nonoverlapping(old_ptr, new_ptr, core::cmp::min(old_size, new_size))
92            };
93        }
94        // Deallocate the old memory
95        unsafe { self.deallocate(pointer, old_layout) };
96
97        Some(memory)
98    }
99
100    /// Returns the amount of memory currently used in this allocator.
101    ///
102    /// # Returns
103    /// The number of bytes currently allocated.
104    ///
105    /// # Safety
106    /// This function is unsafe because it may rely on internal allocator state
107    /// that could be concurrently modified by other threads.
108    unsafe fn get_used(&self) -> usize;
109
110    /// Returns the amount of memory currently available in this allocator.
111    ///
112    /// # Returns
113    /// The number of bytes available for allocation.
114    ///
115    /// # Safety
116    /// This function is unsafe because it may rely on internal allocator state
117    /// that could be concurrently modified by other threads.
118    unsafe fn get_free(&self) -> usize;
119
120    /// Flushes the instruction cache for a specific memory region.
121    ///
122    /// This method ensures that any cached instructions in the specified memory
123    /// region are synchronized with main memory. This is particularly important
124    /// on architectures with separate instruction and data caches when code
125    /// has been modified at runtime.
126    ///
127    /// # Parameters
128    /// * `Address` - Pointer to the start of the memory region to flush
129    /// * `Size` - Size in bytes of the memory region to flush
130    ///
131    /// # Note
132    /// The default implementation does nothing and can be overridden by specific
133    /// allocators that need to handle instruction cache management.
134    fn flush_instruction_cache(&self, _address: NonNull<u8>, _size: usize) {
135        // Default implementation does nothing, can be overridden by specific allocators
136    }
137
138    /// Flushes the data cache to ensure memory coherency.
139    ///
140    /// This method ensures that any cached data is written back to main memory
141    /// and that the cache is synchronized. This is important for maintaining
142    /// memory coherency, especially in multi-core systems or when dealing with
143    /// memory-mapped I/O operations.
144    ///
145    /// # Note
146    /// The default implementation does nothing and can be overridden by specific
147    /// allocators that need to handle data cache management.
148    fn flush_data_cache(&self) {
149        // Default implementation does nothing, can be overridden by specific allocators
150    }
151
152    /// Returns the page size used by this memory allocator.
153    ///
154    /// The page size is the smallest unit of memory that can be allocated
155    /// by the underlying memory management system. This information is useful
156    /// for optimizing memory allocation patterns and understanding alignment
157    /// requirements.
158    ///
159    /// # Returns
160    /// The page size in bytes used by this allocator.
161    ///
162    /// # Note
163    /// The default implementation returns 4096 bytes (4 KiB), which is a common
164    /// page size on many architectures. Specific allocators can override this
165    /// to return the actual page size of their underlying memory management system.
166    fn get_page_size(&self) -> usize {
167        // Default implementation returns a common page size, can be overridden by specific allocators
168        4096 // 4 KiB is a common page size
169    }
170}