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 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
//! 内存分配 API #![stable(feature = "alloc_module", since = "1.28.0")] mod global; mod layout; #[stable(feature = "global_alloc", since = "1.28.0")] pub use self::global::GlobalAlloc; #[stable(feature = "alloc_layout", since = "1.28.0")] pub use self::layout::Layout; #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_deprecated( since = "1.52.0", reason = "Name does not follow std convention, use LayoutError", suggestion = "LayoutError" )] #[allow(deprecated, deprecated_in_future)] pub use self::layout::LayoutErr; #[stable(feature = "alloc_layout_error", since = "1.50.0")] pub use self::layout::LayoutError; use crate::fmt; use crate::ptr::{self, NonNull}; /// `AllocError` 错误表示分配失败,这可能是由于资源耗尽或将给定输入参数与此分配器组合在一起时出错所致。 /// /// /// #[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct AllocError; // (对于 trait 错误的下游隐含我们需要此功能) #[unstable(feature = "allocator_api", issue = "32838")] impl fmt::Display for AllocError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("memory allocation failed") } } /// `Allocator` 的实现可以分配,增长,收缩和释放通过 [`Layout`][] 描述的任意数据块。 /// /// `Allocator` 之所以将其设计为在 ZST,引用或智能指针上实现,是因为如果不更新指向已分配内存的指针,则无法移动具有 `MyAlloc([u8; N])` 之类的分配器。 /// /// 与 [`GlobalAlloc`][] 不同,`Allocator` 允许零大小的分配。 /// 如果基础分配器不支持此功能 (例如 jemalloc) 或返回空指针 (例如 `libc::malloc`),则必须由实现捕获。 /// /// ### 当前分配的内存 /// /// 某些方法要求通过分配器 *currently* 分配存储块。这意味着: /// /// * 该内存块的起始地址先前由 [`allocate`],[`grow`] 或 [`shrink`] 返回,并且 /// /// * 内存块随后并未被释放,其中的块要么通过传递到 [`deallocate`] 直接释放,要么通过传递到返回 `Ok` 的 [`grow`] 或 [`shrink`] 进行了更改。 /// /// 如果 `grow` 或 `shrink` 返回了 `Err`,则传递的指针保持有效。 /// /// [`allocate`]: Allocator::allocate /// [`grow`]: Allocator::grow /// [`shrink`]: Allocator::shrink /// [`deallocate`]: Allocator::deallocate /// /// ### 内存拟合 /// /// 有些方法要求布局适合内存块。 /// 对于到 "fit" 的布局,存储块意味着 (或者,对于到 "fit" 的存储块,布局意味着) 必须满足以下条件: /// /// * 必须以与 [`layout.align()`] 相同的对齐方式分配该块,并且 /// /// * 提供的 [`layout.size()`] 必须在 `min ..= max` 范围内,其中: /// - `min` 是最近用于分配块的布局的大小,并且 /// - `max` 是从 [`allocate`],[`grow`] 或 [`shrink`] 返回的最新实际大小。 /// /// [`layout.align()`]: Layout::align /// [`layout.size()`]: Layout::size /// /// # Safety /// /// * 从分配器返回的内存块必须指向有效内存并保持其有效性,直到丢弃实例及其所有克隆为止, /// /// * 克隆或移动分配器不得使此分配器返回的内存块无效。克隆的分配器的行为必须类似于相同的分配器,并且 /// /// * 指向 [*currently allocated*] 的存储块的任何指针都可以传递给分配器的任何其他方法。 /// /// [*currently allocated*]: #currently-allocated-memory /// /// /// /// /// /// /// /// /// /// /// #[unstable(feature = "allocator_api", issue = "32838")] pub unsafe trait Allocator { /// 尝试分配一块内存。 /// /// 成功后,返回满足 `layout` 大小和对齐保证的 [`NonNull<[u8]>`][NonNull]。 /// /// 返回的块的大小可能大于 `layout.size()` 指定的大小,并且可能已初始化或未初始化其内容。 /// /// # Errors /// /// 返回 `Err` 表示内存已耗尽,或者 `layout` 不满足分配器的大小或对齐约束。 /// /// 鼓励实现在内存耗尽时返回 `Err`,而不是恐慌或终止,但是这不是严格的要求。 /// (具体来说: 在一个底层的原生分配库上实现此 trait 是 *合法的*,该本地分配库在内存耗尽时中止。) /// /// 鼓励希望因分配错误而终止计算的客户调用 [`handle_alloc_error`] 函数,而不是直接调用 `panic!` 或类似方法。 /// /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html /// /// /// fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>; /// 行为类似于 `allocate`,但也确保返回的内存被零初始化。 /// /// # Errors /// /// 返回 `Err` 表示内存已耗尽,或者 `layout` 不满足分配器的大小或对齐约束。 /// /// 鼓励实现在内存耗尽时返回 `Err`,而不是恐慌或终止,但是这不是严格的要求。 /// (具体来说: 在一个底层的原生分配库上实现此 trait 是 *合法的*,该本地分配库在内存耗尽时中止。) /// /// 鼓励希望因分配错误而终止计算的客户调用 [`handle_alloc_error`] 函数,而不是直接调用 `panic!` 或类似方法。 /// /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html /// /// fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { let ptr = self.allocate(layout)?; // SAFETY: `alloc` 返回有效的内存块 unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) } Ok(ptr) } /// 释放 `ptr` 引用的内存。 /// /// # Safety /// /// * `ptr` 必须通过此分配器表示一个内存块 [*currently allocated*],并且 /// * `layout` 必须 [*fit*] 该内存块。 /// /// [*currently allocated*]: #currently-allocated-memory /// [*fit*]: #memory-fitting unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout); /// 尝试扩展内存块。 /// /// 返回一个新的 [`NonNull<[u8]>`][NonNull],其中包含一个指针和分配的内存的实际大小。该指针适用于保存 `new_layout` 描述的数据。 /// 为此,分配器可以扩展 `ptr` 引用的分配以适合新的布局。 /// /// 如果返回 `Ok`,则 `ptr` 引用的内存块的所有权已转移到此分配器。 /// 内存可能已释放,也可能尚未释放,除非已通过此方法的返回值再次将其转移回调用方,否则应将其视为不可用。 /// /// 如果此方法返回 `Err`,则该存储块的所有权尚未转移到此分配器,并且该存储块的内容未更改。 /// /// # Safety /// /// * `ptr` 必须通过此分配器表示一个内存 [*currently allocated*] 块。 /// * `old_layout` 必须 [*fit*] 该内存块 (`new_layout` 参数不需要适合它。)。 /// * `new_layout.size()` 必须大于或等于 `old_layout.size()`。 /// /// [*currently allocated*]: #currently-allocated-memory /// [*fit*]: #memory-fitting /// /// # Errors /// /// 如果新布局不符合分配器的大小和分配器的对齐约束,或者如果增长失败,则返回 `Err`。 /// /// 鼓励实现在内存耗尽时返回 `Err`,而不是恐慌或终止,但是这不是严格的要求。 /// (具体来说: 在一个底层的原生分配库上实现此 trait 是 *合法的*,该本地分配库在内存耗尽时中止。) /// /// 鼓励希望因分配错误而终止计算的客户调用 [`handle_alloc_error`] 函数,而不是直接调用 `panic!` 或类似方法。 /// /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html /// /// /// /// /// /// unsafe fn grow( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" ); let new_ptr = self.allocate(new_layout)?; // SAFETY: 因为 `new_layout.size()` 必须大于或等于 `old_layout.size()`,所以旧的和新的内存分配对于 `old_layout.size()` 字节的读取和写入均有效。 // 另外,由于尚未分配旧分配,因此它不能与 `new_ptr` 重叠。 // 因此,调用 `copy_nonoverlapping` 是安全的。 // 调用者必须遵守 `dealloc` 的安全保证。 // unsafe { ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); self.deallocate(ptr, old_layout); } Ok(new_ptr) } /// 行为类似于 `grow`,但也确保在返回新内容之前将其设置为零。 /// /// 成功调用后,该存储块将包含以下内容 /// `grow_zeroed`: /// * 字节 `0..old_layout.size()` 从原始分配中保留。 /// * 字节 `old_layout.size()..old_size` 将保留还是清零,具体取决于分配器的实现。 /// `old_size` 指的是 `grow_zeroed` 调用之前的内存块的大小,它可能大于分配时最初请求的大小。 /// * 字节 `old_size..new_size` 被清零。`new_size` 是指 `grow_zeroed` 调用返回的存储块的大小。 /// /// # Safety /// /// * `ptr` 必须通过此分配器表示一个内存 [*currently allocated*] 块。 /// * `old_layout` 必须 [*fit*] 该内存块 (`new_layout` 参数不需要适合它。)。 /// * `new_layout.size()` 必须大于或等于 `old_layout.size()`。 /// /// [*currently allocated*]: #currently-allocated-memory /// [*fit*]: #memory-fitting /// /// # Errors /// /// 如果新布局不符合分配器的大小和分配器的对齐约束,或者如果增长失败,则返回 `Err`。 /// /// 鼓励实现在内存耗尽时返回 `Err`,而不是恐慌或终止,但是这不是严格的要求。 /// (具体来说: 在一个底层的原生分配库上实现此 trait 是 *合法的*,该本地分配库在内存耗尽时中止。) /// /// 鼓励希望因分配错误而终止计算的客户调用 [`handle_alloc_error`] 函数,而不是直接调用 `panic!` 或类似方法。 /// /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html /// /// /// /// /// /// unsafe fn grow_zeroed( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" ); let new_ptr = self.allocate_zeroed(new_layout)?; // SAFETY: 因为 `new_layout.size()` 必须大于或等于 `old_layout.size()`,所以旧的和新的内存分配对于 `old_layout.size()` 字节的读取和写入均有效。 // 另外,由于尚未分配旧分配,因此它不能与 `new_ptr` 重叠。 // 因此,调用 `copy_nonoverlapping` 是安全的。 // 调用者必须遵守 `dealloc` 的安全保证。 // unsafe { ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size()); self.deallocate(ptr, old_layout); } Ok(new_ptr) } /// 尝试缩小内存块。 /// /// 返回一个新的 [`NonNull<[u8]>`][NonNull],其中包含一个指针和分配的内存的实际大小。该指针适用于保存 `new_layout` 描述的数据。 /// 为此,分配器可以缩小 `ptr` 引用的分配以适合新的布局。 /// /// 如果返回 `Ok`,则 `ptr` 引用的内存块的所有权已转移到此分配器。 /// 内存可能已释放,也可能尚未释放,除非已通过此方法的返回值再次将其转移回调用方,否则应将其视为不可用。 /// /// 如果此方法返回 `Err`,则该存储块的所有权尚未转移到此分配器,并且该存储块的内容未更改。 /// /// # Safety /// /// * `ptr` 必须通过此分配器表示一个内存 [*currently allocated*] 块。 /// * `old_layout` 必须 [*fit*] 该内存块 (`new_layout` 参数不需要适合它。)。 /// * `new_layout.size()` 必须小于或等于 `old_layout.size()`。 /// /// [*currently allocated*]: #currently-allocated-memory /// [*fit*]: #memory-fitting /// /// # Errors /// /// 如果新的布局不符合分配器的大小和分配器的对齐约束,或者缩小失败,则返回 `Err`。 /// /// 鼓励实现在内存耗尽时返回 `Err`,而不是恐慌或终止,但是这不是严格的要求。 /// (具体来说: 在一个底层的原生分配库上实现此 trait 是 *合法的*,该本地分配库在内存耗尽时中止。) /// /// 鼓励希望因分配错误而终止计算的客户调用 [`handle_alloc_error`] 函数,而不是直接调用 `panic!` 或类似方法。 /// /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html /// /// /// /// /// /// unsafe fn shrink( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError> { debug_assert!( new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" ); let new_ptr = self.allocate(new_layout)?; // SAFETY: 因为 `new_layout.size()` 必须小于或等于 `old_layout.size()`,所以旧的和新的内存分配对于 `new_layout.size()` 字节的读取和写入均有效。 // 另外,由于尚未分配旧分配,因此它不能与 `new_ptr` 重叠。 // 因此,调用 `copy_nonoverlapping` 是安全的。 // 调用者必须遵守 `dealloc` 的安全保证。 // unsafe { ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_layout.size()); self.deallocate(ptr, old_layout); } Ok(new_ptr) } /// 为此 `Allocator` 实例创建 "by reference" 适配器。 /// /// 返回的适配器还实现了 `Allocator`,将仅借用此适配器。 #[inline(always)] fn by_ref(&self) -> &Self where Self: Sized, { self } } #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl<A> Allocator for &A where A: Allocator + ?Sized, { #[inline] fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { (**self).allocate(layout) } #[inline] fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { (**self).allocate_zeroed(layout) } #[inline] unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) { // SAFETY: 调用者必须坚持安全保证 unsafe { (**self).deallocate(ptr, layout) } } #[inline] unsafe fn grow( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError> { // SAFETY: 调用者必须坚持安全保证 unsafe { (**self).grow(ptr, old_layout, new_layout) } } #[inline] unsafe fn grow_zeroed( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError> { // SAFETY: 调用者必须坚持安全保证 unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } } #[inline] unsafe fn shrink( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError> { // SAFETY: 调用者必须坚持安全保证 unsafe { (**self).shrink(ptr, old_layout, new_layout) } } }