1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#![deny(unsafe_op_in_unsafe_fn)]

use crate::alloc::{GlobalAlloc, Layout, System};
use crate::ffi::c_void;
use crate::ptr;
use crate::sync::atomic::{AtomicPtr, Ordering};
use crate::sys::c;
use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN};

#[cfg(test)]
mod tests;

// Windows 上的堆内存管理是通过使用系统 Heap API (heapapi.h) 完成的
// See https://docs.microsoft.com/windows/win32/api/heapapi/

// 指示 `HeapAlloc` 返回的内存应清零的标志。
const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008;

extern "system" {
    // 获取当前进程的默认堆的句柄,如果操作失败,则为 null。
    //
    // SAFETY: 假设在同一进程中成功调用此函数会返回相同的句柄,该句柄在整个进程生命周期内始终有效。
    //
    //
    // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap
    fn GetProcessHeap() -> c::HANDLE;

    // 从给定的堆 `hHeap` 分配一块 `dwBytes` 字节的内存。
    // 如果 `dwFlags` 设置为 `HEAP_ZERO_MEMORY`,分配的内存可能未初始化或归零。
    //
    //
    // 返回指向新分配内存的指针,如果操作失败,则返回 null。
    // 返回的指针将至少与 `MIN_ALIGN` 对齐。
    //
    // SAFETY:
    //  - `hHeap` 必须是 `GetProcessHeap` 返回的非空句柄。
    //  - `dwFlags` 必须设置为零或 `HEAP_ZERO_MEMORY`。
    //
    // 请注意,与其他一些分配器相反,`dwBytes` 可以为零。
    //
    // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc
    fn HeapAlloc(hHeap: c::HANDLE, dwFlags: c::DWORD, dwBytes: c::SIZE_T) -> c::LPVOID;

    // 将给定指针 `lpMem` 后面的一块内存从给定的堆 `hHeap` 重新分配到至少 `dwBytes` 字节的块,要么将块缩小到位,要么在新位置分配,复制内存并释放原始位置。
    //
    //
    // 返回指向重新分配的内存的指针,如果操作失败,则返回 null。
    // 返回的指针将至少与 `MIN_ALIGN` 对齐。
    // 如果操作失败,给定的块将永远不会被释放。
    //
    // SAFETY:
    //  - `hHeap` 必须是 `GetProcessHeap` 返回的非空句柄。
    //  - `dwFlags` 必须设置为零。
    //  - `lpMem` 必须是一个非空指针,指向由 `HeapAlloc` 或 `HeapReAlloc` 返回的尚未被释放的已分配块。
    // 如果块在新位置成功重新分配,则不能再次解引用指向已释放内存的指针,例如 `lpMem`。
    //
    // 请注意,与其他一些分配器相反,`dwBytes` 可以为零。
    //
    // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc
    //
    //
    //
    fn HeapReAlloc(
        hHeap: c::HANDLE,
        dwFlags: c::DWORD,
        lpMem: c::LPVOID,
        dwBytes: c::SIZE_T,
    ) -> c::LPVOID;

    // 从给定的堆 `hHeap` 中释放给定指针 `lpMem` 后面的一块内存。
    // 如果操作成功则返回一个非零值,如果操作失败则返回零。
    //
    // SAFETY:
    //  - `hHeap` 必须是 `GetProcessHeap` 返回的非空句柄。
    //  - `dwFlags` 必须设置为零。
    //  - `lpMem` 必须是 `HeapAlloc` 或 `HeapReAlloc` 返回的一个尚未被释放的已分配块的指针。
    // 如果块被成功释放,指向被释放内存的指针,如 `lpMem`,不得再次解引用。
    //
    //
    // 注意 `lpMem` 允许为空,这不会导致操作失败。
    //
    // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree
    //
    fn HeapFree(hHeap: c::HANDLE, dwFlags: c::DWORD, lpMem: c::LPVOID) -> c::BOOL;
}

// 缓存到当前进程的默认堆的句柄。
// `GetProcessHeap` 返回的非空句柄,或在尚未初始化或 `GetProcessHeap` 失败时为空。
static HEAP: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());

// 获取当前进程的默认堆的句柄,如果操作失败,则为 null。
// 如果此操作成功,则 `HEAP` 将成功初始化并包含 `GetProcessHeap` 返回的非空句柄。
//
#[inline]
fn init_or_get_process_heap() -> c::HANDLE {
    let heap = HEAP.load(Ordering::Relaxed);
    if heap.is_null() {
        // `HEAP` 尚未成功初始化
        let heap = unsafe { GetProcessHeap() };
        if !heap.is_null() {
            // SAFETY: 不需要锁定,因为在同一个进程中,成功调用 `GetProcessHeap` 将始终返回相同的值,即使在不同的线程上也是如此。
            //
            HEAP.store(heap, Ordering::Release);

            // SAFETY: `HEAP` 包含一个由 `GetProcessHeap` 返回的非空句柄
            heap
        } else {
            // 无法获取当前进程堆。
            ptr::null_mut()
        }
    } else {
        // SAFETY: `HEAP` 包含一个由 `GetProcessHeap` 返回的非空句柄
        heap
    }
}

// 获取当前进程默认堆的非空句柄。
// SAFETY: `HEAP` 必须已成功初始化。
#[inline]
unsafe fn get_process_heap() -> c::HANDLE {
    HEAP.load(Ordering::Acquire)
}

// 包含指向已分配块开头的指针的标头。
// SAFETY: 大小和对齐方式必须 <= `MIN_ALIGN`。
#[repr(C)]
struct Header(*mut u8);

// 为给定的 `layout` 分配一块可选为零的内存。
// SAFETY: 返回一个满足 `System` 关于已分配指针的保证的指针,如果操作失败,则返回 null。
// 如果返回非空,那么 `HEAP` 将被成功初始化。
//
#[inline]
unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 {
    let heap = init_or_get_process_heap();
    if heap.is_null() {
        // 分配失败,无法获取当前进程堆。
        return ptr::null_mut();
    }

    // 分配的内存将被清零或未初始化。
    let flags = if zeroed { HEAP_ZERO_MEMORY } else { 0 };

    if layout.align() <= MIN_ALIGN {
        // SAFETY: `heap` 是 `GetProcessHeap` 返回的非空句柄。
        // 返回的指针指向已分配块的开始。
        unsafe { HeapAlloc(heap, flags, layout.size()) as *mut u8 }
    } else {
        // 分配额外的填充以便能够满足对齐。
        let total = layout.align() + layout.size();

        // SAFETY: `heap` 是 `GetProcessHeap` 返回的非空句柄。
        let ptr = unsafe { HeapAlloc(heap, flags, total) as *mut u8 };
        if ptr.is_null() {
            // 分配失败。
            return ptr::null_mut();
        }

        // 从分配的块的开头创建一个正确对齐的指针偏移量,并在它之前写入一个头。
        //

        let offset = layout.align() - (ptr as usize & (layout.align() - 1));
        // SAFETY: `MIN_ALIGN` <= `offset` <= `layout.align()`,分配块的大小为 `layout.align() + layout.size()`。
        // `aligned` 因此将是分配块内正确对齐的指针,其后面至少有 `layout.size()` 字节,前面至少有 `MIN_ALIGN` 字节的填充。
        //
        //
        let aligned = unsafe { ptr.add(offset) };
        // SAFETY: 因为标头的大小和对齐方式 <= `MIN_ALIGN` 并且 `aligned` 至少与 `MIN_ALIGN` 对齐并且在它之前至少有 `MIN_ALIGN` 个字节的填充,所以直接在它之前写入标头是安全的。
        //
        //
        unsafe { ptr::write((aligned as *mut Header).offset(-1), Header(ptr)) };

        // SAFETY: 返回的指针不指向已分配块的开始,但在它包含块开始位置之前有一个直接可读的标头。
        //
        //
        aligned
    }
}

// 此分配器返回的所有指针除了 `GlobalAlloc` 的保证外,还具有以下属性:
//
// 如果使用 `layout` 指定对齐 <= `MIN_ALIGN` 分配或重新分配指针,则指针将至少与 `MIN_ALIGN` 对齐并指向已分配块的开头。
//
// 如果使用 `layout` 指定对齐 > `MIN_ALIGN` 分配或重新分配指针,则指针将与指定对齐对齐,而不指向已分配块的开头。
//
// 相反,在返回的指针之前会有一个直接可读的标头,包含块开始的实际位置。
//
//
//
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
    #[inline]
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        // SAFETY: `allocate` 返回的指针满足 `System` 的保证
        let zeroed = false;
        unsafe { allocate(layout, zeroed) }
    }

    #[inline]
    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
        // SAFETY: `allocate` 返回的指针满足 `System` 的保证
        let zeroed = true;
        unsafe { allocate(layout, zeroed) }
    }

    #[inline]
    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        let block = {
            if layout.align() <= MIN_ALIGN {
                ptr
            } else {
                // 块开始的位置存储在 `ptr` 之前的填充中。

                // SAFETY: 由于 `System` 的约定,`ptr` 被保证为非空并且在它之前有一个直接可读的标头。
                //
                unsafe { ptr::read((ptr as *mut Header).offset(-1)).0 }
            }
        };

        // SAFETY: 因为 `ptr` 已经用这个分配器成功分配了,所以 `HEAP` 一定已经成功初始化了。
        //
        let heap = unsafe { get_process_heap() };

        // SAFETY: `heap` 是 `GetProcessHeap` 返回的非空句柄,`block` 是指向已分配块开头的指针。
        //
        unsafe { HeapFree(heap, 0, block as c::LPVOID) };
    }

    #[inline]
    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
        if layout.align() <= MIN_ALIGN {
            // SAFETY: 因为 `ptr` 已经用这个分配器成功分配了,所以 `HEAP` 一定已经成功初始化了。
            //
            let heap = unsafe { get_process_heap() };

            // SAFETY: `heap` 是 `GetProcessHeap` 返回的非空句柄,`ptr` 是指向已分配块开头的指针。
            //
            // 返回的指针指向已分配块的开始。
            unsafe { HeapReAlloc(heap, 0, ptr as c::LPVOID, new_size) as *mut u8 }
        } else {
            // SAFETY: `realloc_fallback` 使用 `dealloc` 和 `alloc` 实现,它们将正确处理 `ptr` 并返回满足 `System` 保证的指针
            //
            unsafe { realloc_fallback(self, ptr, layout, new_size) }
        }
    }
}