|
1 | 1 | use core::alloc::Layout;
|
| 2 | +use core::mem; |
2 | 3 | use core::mem::{align_of, size_of};
|
3 | 4 | use core::ptr::NonNull;
|
4 | 5 |
|
@@ -55,34 +56,62 @@ impl HoleList {
|
55 | 56 | }
|
56 | 57 | }
|
57 | 58 |
|
| 59 | + /// Align layout. Returns a layout with size increased to |
| 60 | + /// fit at least `HoleList::min_size` and proper alignment of a `Hole`. |
| 61 | + pub fn align_layout(layout: Layout) -> Layout { |
| 62 | + let mut size = layout.size(); |
| 63 | + if size < Self::min_size() { |
| 64 | + size = Self::min_size(); |
| 65 | + } |
| 66 | + let size = align_up(size, mem::align_of::<Hole>()); |
| 67 | + let layout = Layout::from_size_align(size, layout.align()).unwrap(); |
| 68 | + |
| 69 | + layout |
| 70 | + } |
| 71 | + |
58 | 72 | /// Searches the list for a big enough hole. A hole is big enough if it can hold an allocation
|
59 | 73 | /// of `layout.size()` bytes with the given `layout.align()`. If such a hole is found in the
|
60 | 74 | /// list, a block of the required size is allocated from it. Then the start address of that
|
61 |
| - /// block is returned. |
| 75 | + /// block and the aligned layout are returned. The automatic layout alignment is required |
| 76 | + /// because the HoleList has some additional layout requirements for each memory block. |
62 | 77 | /// This function uses the “first fit” strategy, so it uses the first hole that is big
|
63 | 78 | /// enough. Thus the runtime is in O(n) but it should be reasonably fast for small allocations.
|
64 |
| - pub fn allocate_first_fit(&mut self, layout: Layout) -> Result<NonNull<u8>, ()> { |
65 |
| - assert!(layout.size() >= Self::min_size()); |
| 79 | + pub fn allocate_first_fit(&mut self, layout: Layout) -> Result<(NonNull<u8>, Layout), ()> { |
| 80 | + let aligned_layout = Self::align_layout(layout); |
66 | 81 |
|
67 |
| - allocate_first_fit(&mut self.first, layout).map(|allocation| { |
| 82 | + allocate_first_fit(&mut self.first, aligned_layout).map(|allocation| { |
68 | 83 | if let Some(padding) = allocation.front_padding {
|
69 | 84 | deallocate(&mut self.first, padding.addr, padding.size);
|
70 | 85 | }
|
71 | 86 | if let Some(padding) = allocation.back_padding {
|
72 | 87 | deallocate(&mut self.first, padding.addr, padding.size);
|
73 | 88 | }
|
74 |
| - NonNull::new(allocation.info.addr as *mut u8).unwrap() |
| 89 | + |
| 90 | + ( |
| 91 | + NonNull::new(allocation.info.addr as *mut u8).unwrap(), |
| 92 | + aligned_layout, |
| 93 | + ) |
75 | 94 | })
|
76 | 95 | }
|
77 | 96 |
|
78 | 97 | /// Frees the allocation given by `ptr` and `layout`. `ptr` must be a pointer returned by a call
|
79 | 98 | /// to the `allocate_first_fit` function with identical layout. Undefined behavior may occur for
|
80 | 99 | /// invalid arguments.
|
| 100 | + /// The function performs exactly the same layout adjustments as [allocate_first_fit] and |
| 101 | + /// returns the aligned layout. |
81 | 102 | /// This function walks the list and inserts the given block at the correct place. If the freed
|
82 | 103 | /// block is adjacent to another free block, the blocks are merged again.
|
83 | 104 | /// This operation is in `O(n)` since the list needs to be sorted by address.
|
84 |
| - pub unsafe fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) { |
85 |
| - deallocate(&mut self.first, ptr.as_ptr() as usize, layout.size()) |
| 105 | + /// |
| 106 | + /// [allocate_first_fit]: ./struct.HoleList.html#method.allocate_first_fit |
| 107 | + pub unsafe fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) -> Layout { |
| 108 | + let aligned_layout = Self::align_layout(layout); |
| 109 | + deallocate( |
| 110 | + &mut self.first, |
| 111 | + ptr.as_ptr() as usize, |
| 112 | + aligned_layout.size(), |
| 113 | + ); |
| 114 | + aligned_layout |
86 | 115 | }
|
87 | 116 |
|
88 | 117 | /// Returns the minimal allocation size. Smaller allocations or deallocations are not allowed.
|
|
0 commit comments