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}