memory/
trait.rs

1use core::ptr::NonNull;
2
3use crate::{CapabilityFlags, 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: CapabilityFlags, layout: Layout)
33    -> Option<NonNull<u8>>;
34
35    /// Deallocates memory previously allocated by this allocator.
36    ///
37    /// # Parameters
38    /// * `Pointer` - Pointer to the memory to deallocate
39    /// * `Layout` - The layout that was used to allocate the memory
40    ///
41    /// # Safety
42    /// This function is unsafe because the caller must ensure that:
43    /// - The pointer was allocated by this allocator
44    /// - The layout matches the one used for allocation
45    /// - The memory is not used after deallocation
46    /// - The memory is not deallocated multiple times
47    unsafe fn deallocate(&self, pointer: NonNull<u8>, layout: Layout);
48
49    /// Reallocates memory with a new layout.
50    ///
51    /// This method changes the size or alignment of a previously allocated memory block.
52    /// If the pointer is `None`, this behaves like a normal allocation.
53    /// If reallocation is successful, the contents of the old memory are preserved
54    /// up to the minimum of the old and new sizes.
55    ///
56    /// # Parameters
57    /// * `Pointer` - Optional pointer to the memory to reallocate. If `None`, acts as a new allocation
58    /// * `Old_layout` - The layout that was used for the original allocation
59    /// * `New_layout` - The new layout requirements for the memory
60    ///
61    /// # Returns
62    /// A pointer to the reallocated memory with the new layout, or `None` if reallocation failed.
63    /// The protection is set to [`crate::Protection::READ`].
64    ///
65    /// # Safety
66    /// This function is unsafe because the caller must ensure that:
67    /// - If `Pointer` is `Some`, it was allocated by this allocator
68    /// - The `Old_layout` matches the one used for the original allocation
69    /// - The old memory is not used after successful reallocation
70    /// - The returned memory is properly initialized before use
71    unsafe fn reallocate(
72        &self,
73        old_pointer: NonNull<u8>,
74        old_layout: Layout,
75        new_layout: Layout,
76    ) -> Option<NonNull<u8>> {
77        // if old_layout == new_layout {
78        //     return Some(old_pointer);
79        // }
80
81        let new_pointer = unsafe { self.allocate(CapabilityFlags::None, new_layout) }?;
82
83        // Copy the old data to the new memory
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 = old_pointer.as_ptr();
89            let new_ptr = new_pointer.as_ptr();
90            let copy_size = core::cmp::min(old_size, new_size);
91
92            // Check if memory regions overlap
93            let old_end = old_ptr.wrapping_add(old_size);
94            let new_end = new_ptr.wrapping_add(new_size);
95            let overlaps = (old_ptr < new_end) && (new_ptr < old_end);
96
97            if overlaps {
98                // Use copy for overlapping regions (handles overlap correctly)
99                unsafe { core::ptr::copy(old_ptr, new_ptr, copy_size) };
100            } else {
101                // Use copy_nonoverlapping for non-overlapping regions (faster)
102                unsafe { core::ptr::copy_nonoverlapping(old_ptr, new_ptr, copy_size) };
103            }
104        }
105        // Deallocate the old memory
106        unsafe { self.deallocate(old_pointer, old_layout) };
107
108        Some(new_pointer)
109    }
110
111    /// Returns the amount of memory currently used in this allocator.
112    ///
113    /// # Returns
114    /// The number of bytes currently allocated.
115    fn get_used(&self) -> usize;
116
117    /// Returns the amount of memory currently available in this allocator.
118    ///
119    /// # Returns
120    /// The number of bytes available for allocation.
121    fn get_free(&self) -> usize;
122
123    /// Returns the total size of the memory managed by this allocator.
124    ///
125    /// # Returns
126    /// The total number of bytes managed by the allocator.
127    fn get_total_size(&self) -> usize {
128        self.get_used() + self.get_free()
129    }
130
131    /// Flushes the instruction cache for a specific memory region.
132    ///
133    /// This method ensures that any cached instructions in the specified memory
134    /// region are synchronized with main memory. This is particularly important
135    /// on architectures with separate instruction and data caches when code
136    /// has been modified at runtime.
137    ///
138    /// # Parameters
139    /// * `Address` - Pointer to the start of the memory region to flush
140    /// * `Size` - Size in bytes of the memory region to flush
141    ///
142    /// # Note
143    /// The default implementation does nothing and can be overridden by specific
144    /// allocators that need to handle instruction cache management.
145    fn flush_instruction_cache(&self, _address: NonNull<u8>, _size: usize) {
146        // Default implementation does nothing, can be overridden by specific allocators
147    }
148
149    /// Flushes the data cache to ensure memory coherency.
150    ///
151    /// This method ensures that any cached data is written back to main memory
152    /// and that the cache is synchronized. This is important for maintaining
153    /// memory coherency, especially in multi-core systems or when dealing with
154    /// memory-mapped I/O operations.
155    ///
156    /// # Note
157    /// The default implementation does nothing and can be overridden by specific
158    /// allocators that need to handle data cache management.
159    fn flush_data_cache(&self) {
160        // Default implementation does nothing, can be overridden by specific allocators
161    }
162
163    /// Returns the page size used by this memory allocator.
164    ///
165    /// The page size is the smallest unit of memory that can be allocated
166    /// by the underlying memory management system. This information is useful
167    /// for optimizing memory allocation patterns and understanding alignment
168    /// requirements.
169    ///
170    /// # Returns
171    /// The page size in bytes used by this allocator.
172    ///
173    /// # Note
174    /// The default implementation returns 4096 bytes (4 KiB), which is a common
175    /// page size on many architectures. Specific allocators can override this
176    /// to return the actual page size of their underlying memory management system.
177    fn get_page_size(&self) -> usize {
178        // Default implementation returns a common page size, can be overridden by specific allocators
179        4096 // 4 KiB is a common page size
180    }
181}