lite_alloc/single_threaded/freelist.rs
1use crate::PAGE_SIZE;
2use core::{
3 alloc::{GlobalAlloc, Layout},
4 ptr::{self, null_mut},
5};
6
7/// Safety Warning:
8/// Allocators in this module are designed for [Single Threaded] environments.
9/// `Sync` is implemented only to satisfy `GlobalAlloc` trait requirements.
10/// Using this allocator in a multi-threaded environment will lead to Undefined Behavior (UB).
11/// Please ensure it is used only in single-threaded environments (e.g., WASM or single-threaded embedded).
12///
13/// 安全性警示 (Safety Warning):
14/// 本模块中的分配器均为【单线程】设计。
15/// 实现了 `Sync` 仅为了满足 `GlobalAlloc` trait 的要求。
16/// 在多线程环境中使用此分配器会导致未定义行为 (UB)。
17/// 请确保只在单线程环境(如 WASM 或单线程嵌入式环境)中使用。
18unsafe impl Sync for FreeListAllocator {}
19
20/// A non-thread-safe allocator using a free list.
21/// Complexity of allocation and deallocation is O(length of free list).
22///
23/// The free list is sorted by address, and adjacent memory blocks are merged when inserting new blocks.
24///
25/// 一个使用空闲链表的非线程安全分配器。
26/// 分配和释放操作的时间复杂度为 O(空闲链表长度)。
27///
28/// 空闲链表按地址排序,并且在插入新块时会合并相邻的内存块。
29pub struct FreeListAllocator;
30
31// Global State
32static mut FREE_LIST: *mut FreeListNode = EMPTY_FREE_LIST;
33
34impl FreeListAllocator {
35 pub const fn new() -> Self {
36 FreeListAllocator
37 }
38
39 /// Testing only: Reset the internal state.
40 /// Safety: usage is inherently unsafe if allocator is in use.
41 pub unsafe fn reset() {
42 unsafe {
43 FREE_LIST = EMPTY_FREE_LIST;
44 }
45 }
46}
47
48const EMPTY_FREE_LIST: *mut FreeListNode = usize::MAX as *mut FreeListNode;
49
50/// Stored at the beginning of each free segment.
51/// Note: This could be packed into 1 word (using low bits to mark this case,
52/// and only using the second word when allocation size is larger than 1 word).
53///
54/// 存储在每个空闲段的开头。
55/// 注意:可以将其放入 1 个字中(使用低位标记该情况,
56/// 然后仅在分配大小大于 1 个字时使用第二个字)
57struct FreeListNode {
58 next: *mut FreeListNode,
59 size: usize,
60}
61
62const NODE_SIZE: usize = core::mem::size_of::<FreeListNode>();
63
64// Safety: No one else owns the raw pointer (conceptually), logic is same.
65unsafe impl Send for FreeListAllocator {}
66
67unsafe impl GlobalAlloc for FreeListAllocator {
68 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
69 // 1. Force fixed alignment to 16 bytes (covering u8 to u128/v128)
70 // This saves you from complex dynamic alignment logic reading layout.align()
71 // 1. 强制固定对齐为 16 字节 (覆盖 u8 到 u128/v128)
72 // 这样你就不用读取 layout.align() 来做复杂的动态对齐逻辑了
73 const MIN_ALIGN: usize = 16;
74
75 // 2. If user requests more aggressive alignment (e.g. 4KB page alignment), must handle or fail
76 // For size, you can choose not to support alignment > 16 (return null or panic)
77 // 2. 如果用户请求了更变态的对齐 (比如 4KB 对齐的页),必须处理或失败
78 // 为了体积,你可以选择直接不支持超过 16 的对齐(直接返回 null 或 panic)
79 if layout.align() > MIN_ALIGN {
80 return null_mut();
81 }
82
83 // 3. Calculate size: round up to multiple of 16
84 // Assume NODE_SIZE is also 16 bytes or smaller
85 // 3. 计算大小:向上取整到 16 的倍数
86 // 假设 NODE_SIZE 也是 16 字节或更小
87 let size = layout.size().max(NODE_SIZE);
88 // Fast bitwise round up to 16
89 // 快速位运算取整 (等同于 round_up to 16)
90 let size = (size + 15) & !15;
91
92 let mut free_list: *mut *mut FreeListNode = ptr::addr_of_mut!(FREE_LIST);
93 // Search the free list
94 // 搜索空闲链表
95 loop {
96 // SAFETY: Dereferencing free_list is safe
97 // SAFETY: 解引用 free_list 是安全的
98 if unsafe { *free_list == EMPTY_FREE_LIST } {
99 break;
100 }
101
102 let node = unsafe { *free_list };
103 let node_size = unsafe { (*node).size };
104
105 if size <= node_size {
106 let remaining = node_size - size;
107 // If remaining space is large enough, keep it in the list
108 // 如果剩余空间足够大,我们将其保留在链表中
109 if remaining >= NODE_SIZE {
110 unsafe {
111 (*node).size = remaining;
112 return (node as *mut u8).add(remaining);
113 }
114 } else {
115 // Otherwise, allocate the whole block
116 // 否则,整个块都分配出去
117 unsafe {
118 *free_list = (*node).next;
119 return node as *mut u8;
120 }
121 }
122 }
123 // SAFETY: Move to next node.
124 // SAFETY: 移动到下一个节点。
125 unsafe {
126 free_list = ptr::addr_of_mut!((*node).next);
127 }
128 }
129
130 // No space found in free list.
131 // 未在空闲链表中找到空间。
132 let requested_bytes = round_up(size, PAGE_SIZE);
133 // SAFETY: Call global grow_memory (shimmed on non-wasm)
134 let previous_page_count = unsafe { crate::grow_memory(requested_bytes / PAGE_SIZE) };
135 if previous_page_count == usize::MAX {
136 return null_mut();
137 }
138
139 let ptr = (previous_page_count * PAGE_SIZE) as *mut u8;
140 // SAFETY: Recursive call to self to add new memory block.
141 // SAFETY: 递归调用自身,添加新的内存块。
142 unsafe {
143 self.dealloc(
144 ptr,
145 Layout::from_size_align_unchecked(requested_bytes, PAGE_SIZE),
146 );
147 self.alloc(layout)
148 }
149 }
150
151 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
152 debug_assert!(ptr.align_offset(NODE_SIZE) == 0);
153 let ptr = ptr as *mut FreeListNode;
154 let size = full_size(layout);
155 // SAFETY: Pointer arithmetic
156 // SAFETY: 指针算术
157 // Used to merge with the next node if adjacent.
158 // 用于在相邻时与下一个节点合并。
159 let after_new = unsafe { offset_bytes(ptr, size) };
160
161 // SAFETY: Get static mutable pointer
162 // SAFETY: 获取静态可变指针
163 let mut free_list: *mut *mut FreeListNode = ptr::addr_of_mut!(FREE_LIST);
164 // Insert into free list, sorted by pointer descending.
165 // 插入到空闲链表中,该链表按指针降序存储。
166 loop {
167 // SAFETY: Dereference free_list to check if empty or compare address
168 // SAFETY: 解引用 free_list 检查是否为空或比较地址
169 if unsafe { *free_list == EMPTY_FREE_LIST } {
170 // SAFETY: Write new node and insert at head
171 // SAFETY: 写入新节点并插入链表头
172 unsafe {
173 (*ptr).next = EMPTY_FREE_LIST;
174 (*ptr).size = size;
175 *free_list = ptr;
176 }
177 return;
178 }
179
180 // SAFETY: *free_list is a valid node pointer because we checked EMPTY_FREE_LIST above
181 // SAFETY: *free_list 是一个有效的节点指针,因为我们上面检查了 EMPTY_FREE_LIST
182 if unsafe { *free_list == after_new } {
183 // Merge new node into the node after it.
184 // 将新节点合并到此节点之后的节点中。
185
186 // SAFETY: Access fields
187 // SAFETY: 访问字段
188 let new_size = unsafe { size + (**free_list).size };
189 let next = unsafe { (**free_list).next };
190
191 // SAFETY: Check next continuity
192 // SAFETY: 检查 next 连续性
193 if unsafe { next != EMPTY_FREE_LIST && offset_bytes(next, (*next).size) == ptr } {
194 // Merge into the node before this node, and the one after.
195 // 合并到此节点之前的节点,以及之后的节点。
196 // SAFETY: Update next size, remove current node
197 // SAFETY: 更新 next 的大小,移除当前节点
198 unsafe {
199 (*next).size += new_size;
200 *free_list = next;
201 }
202 return;
203 }
204 // Edit node in free list, move its position and update its size.
205 // 编辑空闲链表中的节点,移动其位置并更新其大小。
206 // SAFETY: Pointer operations
207 // SAFETY: 指针操作
208 unsafe {
209 *free_list = ptr;
210 (*ptr).size = new_size;
211 (*ptr).next = next;
212 }
213 return;
214 }
215
216 if unsafe { *free_list < ptr } {
217 // If adjacent, merge to the end of current node
218 // 如果相邻,则合并到当前节点的末尾
219 // SAFETY: ptr comparison and offset_bytes are pointer arithmetic
220 // SAFETY: 这里的 ptr 比较和 offset_bytes 都是指针算术
221 if unsafe { offset_bytes(*free_list, (**free_list).size) == ptr } {
222 // Merge into the node before this node (and potentially after).
223 // 合并到此节点之前的节点,以及之后的节点。
224 // SAFETY: Only need to update size
225 // SAFETY: 只需更新大小
226 unsafe {
227 (**free_list).size += size;
228 }
229 // Since we merged new node to the end of existing node, no need to update pointers, just change size.
230 // 因为我们将新节点合并到现有节点的末尾,所以不需要更新指针,只需更改大小。
231 return;
232 }
233 // Create a new free list node
234 // 创建一个新的空闲链表节点
235 // SAFETY: List insertion
236 // SAFETY: 链表插入
237 unsafe {
238 (*ptr).next = *free_list;
239 (*ptr).size = size;
240 *free_list = ptr;
241 }
242 return;
243 }
244 // SAFETY: Move pointer
245 // SAFETY: 移动指针
246 unsafe {
247 free_list = ptr::addr_of_mut!((**free_list).next);
248 }
249 }
250 }
251 #[cfg(feature = "realloc")]
252 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
253 // 1. Calculate original block size (consistent with alloc/dealloc)
254 // 1. 计算原块大小 (与 alloc/dealloc 一致)
255 let old_size = full_size(layout);
256 // 2. Calculate new block size (aligned)
257 // 2. 计算新块大小 (对齐)
258 let new_full_size = (new_size.max(NODE_SIZE) + 15) & !15;
259
260 // case A: Shrinking
261 if new_full_size <= old_size {
262 let diff = old_size - new_full_size;
263 // If remaining space is large enough, split and free the remainder
264 // 如果剩余空间足够大,切分并释放剩余部分
265 if diff >= NODE_SIZE {
266 unsafe {
267 let remainder = ptr.add(new_full_size);
268 // Construct a Layout for freeing
269 // align=16 is safe because all our blocks are 16-aligned
270 // 构造一个 Layout 用于释放
271 // align=16 是安全的,因为我们所有的块都是 16 对齐
272 let remainder_layout = Layout::from_size_align_unchecked(diff, 16);
273 self.dealloc(remainder, remainder_layout);
274 }
275 }
276 return ptr;
277 }
278
279 // case B: Growing
280 // Try to merge backwards (In-place grow)
281 // Our list is [Sorted Descending by Address]
282 // 尝试向后合并 (In-place grow)
283 // 我们的链表是【地址降序】 (Descending)
284 // Check if `ptr + old_size` is a free node.
285 let needed = new_full_size - old_size;
286 let target_addr = unsafe { ptr.add(old_size) as *mut FreeListNode };
287
288 let mut prev = ptr::addr_of_mut!(FREE_LIST);
289 loop {
290 let curr = unsafe { *prev };
291 if curr == EMPTY_FREE_LIST {
292 break;
293 }
294
295 // List Descending: 2000 -> 1000 -> 500
296 // If curr (2000) > target (1500), continue searching
297 // If curr (1500) == target (1500), found it
298 // If curr (1000) < target (1500), means target is not in list (missed)
299 // 链表降序: 2000 -> 1000 -> 500
300 // 如果 curr (2000) > target (1500),继续找
301 // 如果 curr (1500) == target (1500),找到
302 // 如果 curr (1000) < target (1500),说明 target 不在链表中 (已错过)
303
304 if curr < target_addr {
305 // Missed
306 break;
307 }
308
309 if curr == target_addr {
310 // Found adjacent free block
311 // Check size
312 let node_size = unsafe { (*curr).size };
313 if node_size >= needed {
314 // Merge!
315 // 1. Remove 'curr' from free list
316 unsafe {
317 *prev = (*curr).next;
318 }
319
320 // 2. If 'curr' had extra space, put the remainder back
321 let remaining_in_node = node_size - needed;
322 if remaining_in_node >= NODE_SIZE {
323 unsafe {
324 // Create remainder node
325 let remainder_addr = (curr as *mut u8).add(needed) as *mut FreeListNode;
326 (*remainder_addr).size = remaining_in_node;
327
328 // Insert remainder back into list.
329 // Since remainder_addr > curr (sub-part of curr, higher address).
330 // And *prev points to Next (which is < curr).
331 // So Remainder > Next.
332 // Remainder should replace Curr's position.
333 (*remainder_addr).next = (*curr).next;
334 *prev = remainder_addr;
335 }
336 }
337 return ptr;
338 }
339 // Adjacent block exists but too small.
340 break;
341 }
342
343 // Next
344 unsafe {
345 prev = ptr::addr_of_mut!((*curr).next);
346 }
347 }
348
349 // Default Fallback: Alloc new, Copy, Dealloc old
350 // 默认回退: Alloc new, Copy, Dealloc old
351 unsafe {
352 let new_ptr = self.alloc(Layout::from_size_align_unchecked(new_size, layout.align()));
353 if !new_ptr.is_null() {
354 ptr::copy_nonoverlapping(ptr, new_ptr, layout.size());
355 self.dealloc(ptr, layout);
356 }
357 new_ptr
358 }
359 }
360}
361
362fn full_size(layout: Layout) -> usize {
363 let grown = layout.size().max(NODE_SIZE);
364 (grown + 15) & !15
365}
366
367/// Round up value to the nearest multiple of increment, where increment must be a power of 2.
368/// If `value` is already a multiple of increment, it remains unchanged.
369///
370/// 将值向上取整到增量的最接近倍数,增量必须是 2 的幂。
371/// 如果 `value` 是增量的倍数,则保持不变。
372fn round_up(value: usize, increment: usize) -> usize {
373 debug_assert!(increment.is_power_of_two());
374 // Calculate `value.div_ceil(increment) * increment`,
375 // utilizing the fact that `increment` is always a power of 2 to avoid integer division,
376 // as it is not always optimized away.
377 // 计算 `value.div_ceil(increment) * increment`,
378 // 利用 `increment` 总是 2 的幂这一事实避免使用整数除法,
379 // 因为它并不总是会被优化掉。
380 multiple_below(value + (increment - 1), increment)
381}
382
383/// Round down value to the nearest multiple of increment, where increment must be a power of 2.
384/// If `value` is a multiple of `increment`, it remains unchanged.
385///
386/// 将值向下取整到增量的最接近倍数,增量必须是 2 的幂。
387/// 如果 `value` 是 `increment` 的倍数,则保持不变。
388fn multiple_below(value: usize, increment: usize) -> usize {
389 debug_assert!(increment.is_power_of_two());
390 // Calculate `value / increment * increment`,
391 // utilizing the fact that `increment` is always a power of 2 to avoid integer division,
392 // as it is not always optimized away.
393 // 计算 `value / increment * increment`,
394 // 利用 `increment` 总是 2 的幂这一事实避免使用整数除法,
395 // 因为它并不总是会被优化掉。
396 value & increment.wrapping_neg()
397}
398
399unsafe fn offset_bytes(ptr: *mut FreeListNode, offset: usize) -> *mut FreeListNode {
400 unsafe { (ptr as *mut u8).add(offset) as *mut FreeListNode }
401}
402
403#[cfg(test)]
404mod tests {
405 use super::*;
406 use crate::reset_heap;
407 use std::sync::{Mutex, MutexGuard};
408
409 static TEST_MUTEX: Mutex<()> = Mutex::new(());
410
411 struct SafeAllocator {
412 inner: FreeListAllocator,
413 _guard: MutexGuard<'static, ()>,
414 }
415
416 impl SafeAllocator {
417 fn new() -> Self {
418 let guard = TEST_MUTEX.lock().unwrap();
419 unsafe {
420 FreeListAllocator::reset();
421 reset_heap();
422 Self {
423 inner: FreeListAllocator::new(),
424 _guard: guard,
425 }
426 }
427 }
428
429 fn alloc(&self, layout: Layout) -> *mut u8 {
430 unsafe { self.inner.alloc(layout) }
431 }
432
433 fn dealloc(&self, ptr: *mut u8, layout: Layout) {
434 unsafe { self.inner.dealloc(ptr, layout) }
435 }
436 }
437
438 impl Drop for SafeAllocator {
439 fn drop(&mut self) {
440 unsafe {
441 FreeListAllocator::reset();
442 reset_heap();
443 }
444 }
445 }
446
447 #[test]
448 fn test_basic_allocation() {
449 let allocator = SafeAllocator::new();
450 let layout = Layout::new::<u64>();
451 let ptr = allocator.alloc(layout);
452 assert!(!ptr.is_null());
453
454 unsafe {
455 *ptr.cast::<u64>() = 0xDEADBEEF;
456 assert_eq!(*ptr.cast::<u64>(), 0xDEADBEEF);
457 }
458
459 allocator.dealloc(ptr, layout);
460 }
461
462 #[test]
463 fn test_allocation_order_descending() {
464 let allocator = SafeAllocator::new();
465 let layout = Layout::from_size_align(16, 16).unwrap();
466
467 // 第一次分配,会触发 gro_memory (64KB),并全部加到 FreeList (Head -> Node(size=64KB))
468 // alloc 会找到这个大块,由于 size 小,它会在 high address 切个块出来给你?
469 // 让我们看看 alloc 逻辑:
470 // if remaining >= NODE_SIZE { (*node).size = remaining; return node + remaining; }
471 // 意味着,它保留了 block 的低地址部分作为新的 FreeNode,返回了高地址部分给用户。
472 // 所以第一个分配的地址应该是 PageEnd - 16。
473 let ptr1 = allocator.alloc(layout);
474
475 // 第二次分配,应当再次从那个剩余的 FreeNode (位于低地址) 的末尾切一块。
476 // 所以 ptr2 应该在 ptr1 之前。
477 let ptr2 = allocator.alloc(layout);
478
479 assert!(!ptr1.is_null());
480 assert!(!ptr2.is_null());
481
482 // 指针地址应该递减:ptr1 > ptr2
483 assert!(ptr1 > ptr2);
484
485 // 它们应该是紧挨著的:Block: [... | ptr2 | ptr1 | end]
486 assert_eq!(ptr1 as usize - ptr2 as usize, 16);
487 }
488
489 #[test]
490 fn test_reuse() {
491 let allocator = SafeAllocator::new();
492 let layout = Layout::from_size_align(16, 16).unwrap();
493
494 let ptr1 = allocator.alloc(layout);
495 allocator.dealloc(ptr1, layout);
496
497 // 释放后,ptr1 对应的块被放回 FreeList。
498 // 因为它是刚刚分配出的最高地址块,且我们没有其他操作,它应该会被合并回大块,或者作为独立块。
499 // 再次分配时,应该优先使用最高地址的块 (Search order)。
500 // 如果合并回去,它就成了大块的一部分(高位),下次 alloc 切割时,会切同样的地址。
501 // 如果没合并(中间有断开),它可能是独立的 Node。 Search 顺序是 Descending。
502 // 大块在低地址,ptr1 在高地址。 Search 先遇到 ptr1 还是 大块?
503 // FreeList 是 Descending。所以先遇到 ptr1。
504 // 如果 ptr1 能满足大小,就用 ptr1。
505 let ptr2 = allocator.alloc(layout);
506 assert_eq!(ptr1, ptr2);
507 }
508
509 #[test]
510 fn test_coalescing_merge() {
511 let allocator = SafeAllocator::new();
512 let layout = Layout::from_size_align(128, 16).unwrap();
513
514 // 分配 3 个块。顺序:ptr1 (high) > ptr2 > ptr3 (low)
515 let ptr1 = allocator.alloc(layout);
516 let ptr2 = allocator.alloc(layout);
517 let ptr3 = allocator.alloc(layout);
518
519 assert_eq!(ptr1 as usize - ptr2 as usize, 128);
520 assert_eq!(ptr2 as usize - ptr3 as usize, 128);
521
522 // 释放中间的 (ptr2)。
523 // 此时 FreeList: [...BigBlock..., ptr2] (如果没和其他合并)
524 allocator.dealloc(ptr2, layout);
525
526 // 释放顶部的 (ptr1)。
527 // ptr1 > ptr2. ptr1 是高地址。
528 // dealloc 插入 ptr1。它发现 ptr1 > ptr2 (Head).
529 // 且 ptr2 + 128 == ptr1。应该导致 ptr2 (Node) 扩展大小,吞并 ptr1。
530 // 结果: FreeList 中有一个合并后的块 [ptr2, ptr1] (Size 256).
531 allocator.dealloc(ptr1, layout);
532
533 // 释放底部的 (ptr3)。
534 // ptr3 < ptr2.
535 // 插入 ptr3。找到 Head (ptr2_ptr1_merged).
536 // 检查: ptr3 + 128 == ptr2 ? Yes.
537 // 合并:ptr3 变成新的 Head,Size += 256. -> [ptr3, ptr2, ptr1] (Size 384).
538 allocator.dealloc(ptr3, layout);
539
540 // 现在申请一个 384 字节的大块。
541 // 它应该完美匹配我们刚刚合并出的那个块 (位于 ptr3)。
542 let layout_large = Layout::from_size_align(384, 16).unwrap();
543 let ptr_large = allocator.alloc(layout_large);
544
545 assert!(!ptr_large.is_null());
546 // 如果 alloc 逻辑是 "如果正好大小相等,就移除节点并返回整个节点指针",
547 // 那么应该返 ptr3。
548 // 如果 alloc 逻辑是 "如果剩余 >= NODE_SIZE 才分割",这里剩余 0,所以不分割。
549 // 直接返回节点地址。
550 assert_eq!(ptr_large, ptr3);
551 }
552
553 #[test]
554 fn test_memory_growth_multi_page() {
555 let allocator = SafeAllocator::new();
556 // 分配 40KB。
557 let layout = Layout::from_size_align(40 * 1024, 16).unwrap();
558 let ptr1 = allocator.alloc(layout);
559 assert!(!ptr1.is_null());
560
561 // 再分配 40KB。当前页 (64KB) 剩 24KB,不够。
562 // 触发 grow_memory。
563 // 分配器会申请新页。返回新页的高地址部分。
564 let ptr2 = allocator.alloc(layout);
565 assert!(!ptr2.is_null());
566 assert_ne!(ptr1, ptr2);
567
568 // 两个指针应该相距较远 (至少 40KB)
569 let dist = if ptr1 > ptr2 {
570 ptr1 as usize - ptr2 as usize
571 } else {
572 ptr2 as usize - ptr1 as usize
573 };
574 assert!(dist >= 40 * 1024);
575 }
576
577 #[test]
578 fn test_alignment_large() {
579 let allocator = SafeAllocator::new();
580 // 目前实现只支持 max align 16。
581 // 如果请求 32,应该返回 null。 (代码里写了 check)
582 let layout_bad = Layout::from_size_align(32, 32).unwrap();
583 let ptr = allocator.alloc(layout_bad);
584 assert!(ptr.is_null());
585 }
586
587 #[test]
588 fn test_fragmentation_fill_hole() {
589 let allocator = SafeAllocator::new();
590 let layout = Layout::from_size_align(64, 16).unwrap();
591
592 // Pointers decrease: p1 > p2 > p3
593 let _p1 = allocator.alloc(layout);
594 let p2 = allocator.alloc(layout);
595 let p3 = allocator.alloc(layout);
596
597 // Free p2. 制造一个 64B 的空洞。
598 allocator.dealloc(p2, layout);
599
600 // 再分配 64B。
601 // Allocator 策略是遍历 FreeList (Descending)。
602 // 列表里有: [Head (Rest of Memory at Low Address), p2 (at Higher Address)]
603 // p2 > Head. 所以 p2 排在前面。
604 // alloc 查 p2,大小正好。
605 // 应该重用 p2。
606
607 let p4 = allocator.alloc(layout);
608 assert_eq!(p4, p2);
609
610 // 确保 p3 还没被分配出去
611 let _p5 = allocator.alloc(layout);
612 // p5 应该是从 Head (Low Address) 切出来的,应该小于 p3
613 assert!(_p5 < p3);
614 }
615 #[test]
616 fn test_realloc_shrink_in_place() {
617 let allocator = SafeAllocator::new();
618 let layout = Layout::from_size_align(128, 16).unwrap();
619 let ptr = allocator.alloc(layout);
620 assert!(!ptr.is_null());
621
622 unsafe {
623 ptr.write_bytes(0xAA, layout.size());
624 }
625
626 // Shrink to 64
627 let new_size = 64;
628 let new_layout = Layout::from_size_align(new_size, 16).unwrap();
629
630 // We need to call realloc from GlobalAlloc trait
631 let realloc_ptr = unsafe { allocator.inner.realloc(ptr, layout, new_size) };
632
633 // Custom impl is In-Place.
634 #[cfg(feature = "realloc")]
635 assert_eq!(ptr, realloc_ptr);
636
637 #[cfg(not(feature = "realloc"))]
638 assert_ne!(ptr, realloc_ptr);
639
640 unsafe {
641 assert_eq!(*realloc_ptr.cast::<u8>(), 0xAA);
642 }
643
644 allocator.dealloc(realloc_ptr, new_layout);
645 }
646
647 #[test]
648 fn test_realloc_grow_in_place() {
649 let allocator = SafeAllocator::new();
650 let layout = Layout::from_size_align(64, 16).unwrap();
651
652 let ptr1 = allocator.alloc(layout);
653 let ptr2 = allocator.alloc(layout);
654
655 assert_eq!(ptr1 as usize - ptr2 as usize, 64);
656
657 allocator.dealloc(ptr1, layout);
658
659 let new_size = 128;
660 let ptr2_new = unsafe { allocator.inner.realloc(ptr2, layout, new_size) };
661
662 #[cfg(feature = "realloc")]
663 assert_eq!(ptr2, ptr2_new);
664
665 #[cfg(not(feature = "realloc"))]
666 assert_ne!(ptr2, ptr2_new);
667
668 allocator.dealloc(ptr2_new, Layout::from_size_align(new_size, 16).unwrap());
669 }
670}