diff options
Diffstat (limited to 'library/std/src')
| -rw-r--r-- | library/std/src/ffi/os_str.rs | 4 | ||||
| -rw-r--r-- | library/std/src/io/mod.rs | 6 | ||||
| -rw-r--r-- | library/std/src/keyword_docs.rs | 27 | ||||
| -rw-r--r-- | library/std/src/lib.rs | 3 | ||||
| -rw-r--r-- | library/std/src/path.rs | 14 | ||||
| -rw-r--r-- | library/std/src/prelude/mod.rs | 64 | ||||
| -rw-r--r-- | library/std/src/sync/once.rs | 2 | ||||
| -rw-r--r-- | library/std/src/sys/windows/alloc.rs | 237 | ||||
| -rw-r--r-- | library/std/src/sys/windows/alloc/tests.rs | 9 | ||||
| -rw-r--r-- | library/std/src/sys/windows/c.rs | 7 |
10 files changed, 285 insertions, 88 deletions
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 14914287cb1..ecaab670349 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -361,7 +361,7 @@ impl OsString { impl From<String> for OsString { /// Converts a [`String`] into a [`OsString`]. /// - /// The conversion copies the data, and includes an allocation on the heap. + /// This conversion does not allocate or copy memory. #[inline] fn from(s: String) -> OsString { OsString { inner: Buf::from_string(s) } @@ -858,7 +858,7 @@ impl From<Cow<'_, OsStr>> for Box<OsStr> { #[stable(feature = "os_string_from_box", since = "1.18.0")] impl From<Box<OsStr>> for OsString { - /// Converts a [`Box`]`<`[`OsStr`]`>` into a `OsString` without copying or + /// Converts a [`Box`]`<`[`OsStr`]`>` into an [`OsString`] without copying or /// allocating. #[inline] fn from(boxed: Box<OsStr>) -> OsString { diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 9953bcd556d..9625984195b 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -505,7 +505,8 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [ /// [`std::io`]: self /// [`File`]: crate::fs::File #[stable(feature = "rust1", since = "1.0.0")] -#[doc(spotlight)] +#[cfg_attr(bootstrap, doc(spotlight))] +#[cfg_attr(not(bootstrap), doc(notable_trait))] pub trait Read { /// Pull some bytes from this source into the specified buffer, returning /// how many bytes were read. @@ -1296,7 +1297,8 @@ impl Initializer { /// /// [`write_all`]: Write::write_all #[stable(feature = "rust1", since = "1.0.0")] -#[doc(spotlight)] +#[cfg_attr(bootstrap, doc(spotlight))] +#[cfg_attr(not(bootstrap), doc(notable_trait))] pub trait Write { /// Write a buffer into this writer, returning how many bytes were written. /// diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index e64cbc18bf7..2a3d44fb17d 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -547,15 +547,18 @@ mod fn_keyword {} /// # fn code() { } /// # let iterator = 0..2; /// { -/// let mut _iter = std::iter::IntoIterator::into_iter(iterator); -/// loop { -/// match _iter.next() { -/// Some(loop_variable) => { -/// code() -/// }, -/// None => break, -/// } -/// } +/// let result = match IntoIterator::into_iter(iterator) { +/// mut iter => loop { +/// let next; +/// match iter.next() { +/// Some(val) => next = val, +/// None => break, +/// }; +/// let loop_variable = next; +/// let () = { code(); }; +/// }, +/// }; +/// result /// } /// ``` /// @@ -1310,7 +1313,11 @@ mod return_keyword {} /// [Reference]: ../reference/items/associated-items.html#methods mod self_keyword {} -#[doc(keyword = "Self")] +// FIXME: Once rustdoc can handle URL conflicts on case insensitive file systems, we can remove the +// three next lines and put back: `#[doc(keyword = "Self")]`. +#[doc(alias = "Self")] +#[allow(rustc::existing_doc_keyword)] +#[doc(keyword = "SelfTy")] // /// The implementing type within a [`trait`] or [`impl`] block, or the current type within a type /// definition. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 3719eeb1840..d5ba2d36346 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -257,7 +257,8 @@ #![feature(doc_cfg)] #![feature(doc_keyword)] #![feature(doc_masked)] -#![feature(doc_spotlight)] +#![cfg_attr(bootstrap, feature(doc_spotlight))] +#![cfg_attr(not(bootstrap), feature(doc_notable_trait))] #![feature(dropck_eyepatch)] #![feature(duration_constants)] #![feature(duration_zero)] diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 57c892f32b1..f4020a42879 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1467,7 +1467,7 @@ impl<T: ?Sized + AsRef<OsStr>> From<&T> for PathBuf { #[stable(feature = "rust1", since = "1.0.0")] impl From<OsString> for PathBuf { - /// Converts a `OsString` into a `PathBuf` + /// Converts an [`OsString`] into a [`PathBuf`] /// /// This conversion does not allocate or copy memory. #[inline] @@ -1478,7 +1478,7 @@ impl From<OsString> for PathBuf { #[stable(feature = "from_path_buf_for_os_string", since = "1.14.0")] impl From<PathBuf> for OsString { - /// Converts a `PathBuf` into a `OsString` + /// Converts a [`PathBuf`] into an [`OsString`] /// /// This conversion does not allocate or copy memory. #[inline] @@ -1489,7 +1489,7 @@ impl From<PathBuf> for OsString { #[stable(feature = "rust1", since = "1.0.0")] impl From<String> for PathBuf { - /// Converts a `String` into a `PathBuf` + /// Converts a [`String`] into a [`PathBuf`] /// /// This conversion does not allocate or copy memory. #[inline] @@ -1595,7 +1595,7 @@ impl<'a> From<Cow<'a, Path>> for PathBuf { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<PathBuf> for Arc<Path> { - /// Converts a `PathBuf` into an `Arc` by moving the `PathBuf` data into a new `Arc` buffer. + /// Converts a [`PathBuf`] into an [`Arc`] by moving the [`PathBuf`] data into a new [`Arc`] buffer. #[inline] fn from(s: PathBuf) -> Arc<Path> { let arc: Arc<OsStr> = Arc::from(s.into_os_string()); @@ -1605,7 +1605,7 @@ impl From<PathBuf> for Arc<Path> { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&Path> for Arc<Path> { - /// Converts a `Path` into an `Arc` by copying the `Path` data into a new `Arc` buffer. + /// Converts a [`Path`] into an [`Arc`] by copying the [`Path`] data into a new [`Arc`] buffer. #[inline] fn from(s: &Path) -> Arc<Path> { let arc: Arc<OsStr> = Arc::from(s.as_os_str()); @@ -1615,7 +1615,7 @@ impl From<&Path> for Arc<Path> { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<PathBuf> for Rc<Path> { - /// Converts a `PathBuf` into an `Rc` by moving the `PathBuf` data into a new `Rc` buffer. + /// Converts a [`PathBuf`] into an [`Rc`] by moving the [`PathBuf`] data into a new `Rc` buffer. #[inline] fn from(s: PathBuf) -> Rc<Path> { let rc: Rc<OsStr> = Rc::from(s.into_os_string()); @@ -1625,7 +1625,7 @@ impl From<PathBuf> for Rc<Path> { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&Path> for Rc<Path> { - /// Converts a `Path` into an `Rc` by copying the `Path` data into a new `Rc` buffer. + /// Converts a [`Path`] into an [`Rc`] by copying the [`Path`] data into a new `Rc` buffer. #[inline] fn from(s: &Path) -> Rc<Path> { let rc: Rc<OsStr> = Rc::from(s.as_os_str()); diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 505b5f3013b..1b4facdd049 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -28,53 +28,53 @@ //! The current version of the prelude (version 1) lives in //! [`std::prelude::v1`], and re-exports the following: //! -//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`], [`Unpin`]}: +//! * <code>[std::marker]::{[Copy], [Send], [Sized], [Sync], [Unpin]}</code>, //! marker traits that indicate fundamental properties of types. -//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}: various +//! * <code>[std::ops]::{[Drop], [Fn], [FnMut], [FnOnce]}</code>, various //! operations for both destructors and overloading `()`. -//! * [`std::mem`]::[`drop`][`mem::drop`]: a convenience function for explicitly +//! * <code>[std::mem]::[drop][mem::drop]</code>, a convenience function for explicitly //! dropping a value. -//! * [`std::boxed`]::[`Box`]: a way to allocate values on the heap. -//! * [`std::borrow`]::[`ToOwned`]: the conversion trait that defines +//! * <code>[std::boxed]::[Box]</code>, a way to allocate values on the heap. +//! * <code>[std::borrow]::[ToOwned]</code>, the conversion trait that defines //! [`to_owned`], the generic method for creating an owned type from a //! borrowed type. -//! * [`std::clone`]::[`Clone`]: the ubiquitous trait that defines -//! [`clone`][`Clone::clone`], the method for producing a copy of a value. -//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]}: the +//! * <code>[std::clone]::[Clone]</code>, the ubiquitous trait that defines +//! [`clone`][Clone::clone], the method for producing a copy of a value. +//! * <code>[std::cmp]::{[PartialEq], [PartialOrd], [Eq], [Ord]}</code>, the //! comparison traits, which implement the comparison operators and are often //! seen in trait bounds. -//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}: generic +//! * <code>[std::convert]::{[AsRef], [AsMut], [Into], [From]}</code>, generic //! conversions, used by savvy API authors to create overloaded methods. -//! * [`std::default`]::[`Default`], types that have default values. -//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`], -//! [`DoubleEndedIterator`], [`ExactSizeIterator`]}: iterators of various +//! * <code>[std::default]::[Default]</code>, types that have default values. +//! * <code>[std::iter]::{[Iterator], [Extend], [IntoIterator], [DoubleEndedIterator], [ExactSizeIterator]}</code>, +//! iterators of various //! kinds. -//! * [`std::option`]::[`Option`]::{[`self`][`Option`], [`Some`], [`None`]}, a +//! * <code>[std::option]::[Option]::{[self][Option], [Some], [None]}</code>, a //! type which expresses the presence or absence of a value. This type is so //! commonly used, its variants are also exported. -//! * [`std::result`]::[`Result`]::{[`self`][`Result`], [`Ok`], [`Err`]}: a type +//! * <code>[std::result]::[Result]::{[self][Result], [Ok], [Err]}</code>, a type //! for functions that may succeed or fail. Like [`Option`], its variants are //! exported as well. -//! * [`std::string`]::{[`String`], [`ToString`]}: heap-allocated strings. -//! * [`std::vec`]::[`Vec`]: a growable, heap-allocated vector. +//! * <code>[std::string]::{[String], [ToString]}</code>, heap-allocated strings. +//! * <code>[std::vec]::[Vec]</code>, a growable, heap-allocated vector. //! -//! [`mem::drop`]: crate::mem::drop -//! [`std::borrow`]: crate::borrow -//! [`std::boxed`]: crate::boxed -//! [`std::clone`]: crate::clone -//! [`std::cmp`]: crate::cmp -//! [`std::convert`]: crate::convert -//! [`std::default`]: crate::default -//! [`std::iter`]: crate::iter -//! [`std::marker`]: crate::marker -//! [`std::mem`]: crate::mem -//! [`std::ops`]: crate::ops -//! [`std::option`]: crate::option +//! [mem::drop]: crate::mem::drop +//! [std::borrow]: crate::borrow +//! [std::boxed]: crate::boxed +//! [std::clone]: crate::clone +//! [std::cmp]: crate::cmp +//! [std::convert]: crate::convert +//! [std::default]: crate::default +//! [std::iter]: crate::iter +//! [std::marker]: crate::marker +//! [std::mem]: crate::mem +//! [std::ops]: crate::ops +//! [std::option]: crate::option //! [`std::prelude::v1`]: v1 -//! [`std::result`]: crate::result -//! [`std::slice`]: crate::slice -//! [`std::string`]: crate::string -//! [`std::vec`]: mod@crate::vec +//! [std::result]: crate::result +//! [std::slice]: crate::slice +//! [std::string]: crate::string +//! [std::vec]: mod@crate::vec //! [`to_owned`]: crate::borrow::ToOwned::to_owned //! [book-closures]: ../../book/ch13-01-closures.html //! [book-dtor]: ../../book/ch15-03-drop.html diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 2e5f843fc43..a24a5cb2ae3 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -471,7 +471,7 @@ fn wait(state_and_queue: &AtomicUsize, mut current_state: usize) { // If the managing thread happens to signal and unpark us before we // can park ourselves, the result could be this thread never gets // unparked. Luckily `park` comes with the guarantee that if it got - // an `unpark` just before on an unparked thread is does not park. + // an `unpark` just before on an unparked thread it does not park. thread::park(); } break; diff --git a/library/std/src/sys/windows/alloc.rs b/library/std/src/sys/windows/alloc.rs index 99b4d6c72a0..af93cd7a3e2 100644 --- a/library/std/src/sys/windows/alloc.rs +++ b/library/std/src/sys/windows/alloc.rs @@ -1,61 +1,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}; -#[repr(C)] -struct Header(*mut u8); +#[cfg(test)] +mod tests; + +// Heap memory management on Windows is done by using the system Heap API (heapapi.h) +// See https://docs.microsoft.com/windows/win32/api/heapapi/ + +// Flag to indicate that the memory returned by `HeapAlloc` should be zeroed. +const HEAP_ZERO_MEMORY: c::DWORD = 0x00000008; -unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { - &mut *(ptr as *mut Header).offset(-1) +extern "system" { + // Get a handle to the default heap of the current process, or null if the operation fails. + // + // SAFETY: Successful calls to this function within the same process are assumed to + // always return the same handle, which remains valid for the entire lifetime of the process. + // + // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap + fn GetProcessHeap() -> c::HANDLE; + + // Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`. + // The allocated memory may be uninitialized, or zeroed if `dwFlags` is + // set to `HEAP_ZERO_MEMORY`. + // + // Returns a pointer to the newly-allocated memory or null if the operation fails. + // The returned pointer will be aligned to at least `MIN_ALIGN`. + // + // SAFETY: + // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. + // - `dwFlags` must be set to either zero or `HEAP_ZERO_MEMORY`. + // + // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. + // + // 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; + + // Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`, + // to a block of at least `dwBytes` bytes, either shrinking the block in place, + // or allocating at a new location, copying memory, and freeing the original location. + // + // Returns a pointer to the reallocated memory or null if the operation fails. + // The returned pointer will be aligned to at least `MIN_ALIGN`. + // If the operation fails the given block will never have been freed. + // + // SAFETY: + // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. + // - `dwFlags` must be set to zero. + // - `lpMem` must be a non-null pointer to an allocated block returned by `HeapAlloc` or + // `HeapReAlloc`, that has not already been freed. + // If the block was successfully reallocated at a new location, pointers pointing to + // the freed memory, such as `lpMem`, must not be dereferenced ever again. + // + // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. + // + // 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; + + // Free a block of memory behind a given pointer `lpMem` from a given heap `hHeap`. + // Returns a nonzero value if the operation is successful, and zero if the operation fails. + // + // SAFETY: + // - `hHeap` must be a non-null handle returned by `GetProcessHeap`. + // - `dwFlags` must be set to zero. + // - `lpMem` must be a pointer to an allocated block returned by `HeapAlloc` or `HeapReAlloc`, + // that has not already been freed. + // If the block was successfully freed, pointers pointing to the freed memory, such as `lpMem`, + // must not be dereferenced ever again. + // + // Note that `lpMem` is allowed to be null, which will not cause the operation to fail. + // + // 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; } -unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 { - let aligned = ptr.add(align - (ptr as usize & (align - 1))); - *get_header(aligned) = Header(ptr); - aligned +// Cached handle to the default heap of the current process. +// Either a non-null handle returned by `GetProcessHeap`, or null when not yet initialized or `GetProcessHeap` failed. +static HEAP: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); + +// Get a handle to the default heap of the current process, or null if the operation fails. +// If this operation is successful, `HEAP` will be successfully initialized and contain +// a non-null handle returned by `GetProcessHeap`. +#[inline] +fn init_or_get_process_heap() -> c::HANDLE { + let heap = HEAP.load(Ordering::Relaxed); + if heap.is_null() { + // `HEAP` has not yet been successfully initialized + let heap = unsafe { GetProcessHeap() }; + if !heap.is_null() { + // SAFETY: No locking is needed because within the same process, + // successful calls to `GetProcessHeap` will always return the same value, even on different threads. + HEAP.store(heap, Ordering::Release); + + // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap` + heap + } else { + // Could not get the current process heap. + ptr::null_mut() + } + } else { + // SAFETY: `HEAP` contains a non-null handle returned by `GetProcessHeap` + heap + } } +// Get a non-null handle to the default heap of the current process. +// SAFETY: `HEAP` must have been successfully initialized. #[inline] -unsafe fn allocate_with_flags(layout: Layout, flags: c::DWORD) -> *mut u8 { - if layout.align() <= MIN_ALIGN { - return c::HeapAlloc(c::GetProcessHeap(), flags, layout.size()) as *mut u8; +unsafe fn get_process_heap() -> c::HANDLE { + HEAP.load(Ordering::Acquire) +} + +// Header containing a pointer to the start of an allocated block. +// SAFETY: Size and alignment must be <= `MIN_ALIGN`. +#[repr(C)] +struct Header(*mut u8); + +// Allocate a block of optionally zeroed memory for a given `layout`. +// SAFETY: Returns a pointer satisfying the guarantees of `System` about allocated pointers, +// or null if the operation fails. If this returns non-null `HEAP` will have been successfully +// initialized. +#[inline] +unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 { + let heap = init_or_get_process_heap(); + if heap.is_null() { + // Allocation has failed, could not get the current process heap. + return ptr::null_mut(); } - let size = layout.size() + layout.align(); - let ptr = c::HeapAlloc(c::GetProcessHeap(), flags, size); - if ptr.is_null() { ptr as *mut u8 } else { align_ptr(ptr as *mut u8, layout.align()) } + // Allocated memory will be either zeroed or uninitialized. + let flags = if zeroed { HEAP_ZERO_MEMORY } else { 0 }; + + if layout.align() <= MIN_ALIGN { + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. + // The returned pointer points to the start of an allocated block. + unsafe { HeapAlloc(heap, flags, layout.size()) as *mut u8 } + } else { + // Allocate extra padding in order to be able to satisfy the alignment. + let total = layout.align() + layout.size(); + + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`. + let ptr = unsafe { HeapAlloc(heap, flags, total) as *mut u8 }; + if ptr.is_null() { + // Allocation has failed. + return ptr::null_mut(); + } + + // Create a correctly aligned pointer offset from the start of the allocated block, + // and write a header before it. + + let offset = layout.align() - (ptr as usize & (layout.align() - 1)); + // SAFETY: `MIN_ALIGN` <= `offset` <= `layout.align()` and the size of the allocated + // block is `layout.align() + layout.size()`. `aligned` will thus be a correctly aligned + // pointer inside the allocated block with at least `layout.size()` bytes after it and at + // least `MIN_ALIGN` bytes of padding before it. + let aligned = unsafe { ptr.add(offset) }; + // SAFETY: Because the size and alignment of a header is <= `MIN_ALIGN` and `aligned` + // is aligned to at least `MIN_ALIGN` and has at least `MIN_ALIGN` bytes of padding before + // it, it is safe to write a header directly before it. + unsafe { ptr::write((aligned as *mut Header).offset(-1), Header(ptr)) }; + + // SAFETY: The returned pointer does not point to the to the start of an allocated block, + // but there is a header readable directly before it containing the location of the start + // of the block. + aligned + } } +// All pointers returned by this allocator have, in addition to the guarantees of `GlobalAlloc`, the +// following properties: +// +// If the pointer was allocated or reallocated with a `layout` specifying an alignment <= `MIN_ALIGN` +// the pointer will be aligned to at least `MIN_ALIGN` and point to the start of the allocated block. +// +// If the pointer was allocated or reallocated with a `layout` specifying an alignment > `MIN_ALIGN` +// the pointer will be aligned to the specified alignment and not point to the start of the allocated block. +// Instead there will be a header readable directly before the returned pointer, containing the actual +// location of the start of the block. #[stable(feature = "alloc_system_type", since = "1.28.0")] unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, 0) + // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` + let zeroed = false; + unsafe { allocate(layout, zeroed) } } #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - allocate_with_flags(layout, c::HEAP_ZERO_MEMORY) + // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` + let zeroed = true; + unsafe { allocate(layout, zeroed) } } #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - if layout.align() <= MIN_ALIGN { - let err = c::HeapFree(c::GetProcessHeap(), 0, ptr as c::LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", c::GetLastError()); - } else { - let header = get_header(ptr); - let err = c::HeapFree(c::GetProcessHeap(), 0, header.0 as c::LPVOID); - debug_assert!(err != 0, "Failed to free heap memory: {}", c::GetLastError()); - } + let block = { + if layout.align() <= MIN_ALIGN { + ptr + } else { + // The location of the start of the block is stored in the padding before `ptr`. + + // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null + // and have a header readable directly before it. + unsafe { ptr::read((ptr as *mut Header).offset(-1)).0 } + } + }; + + // SAFETY: because `ptr` has been successfully allocated with this allocator, + // `HEAP` must have been successfully initialized. + let heap = unsafe { get_process_heap() }; + + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, + // `block` is a pointer to the start of an allocated 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 { - c::HeapReAlloc(c::GetProcessHeap(), 0, ptr as c::LPVOID, new_size) as *mut u8 + // SAFETY: because `ptr` has been successfully allocated with this allocator, + // `HEAP` must have been successfully initialized. + let heap = unsafe { get_process_heap() }; + + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, + // `ptr` is a pointer to the start of an allocated block. + // The returned pointer points to the start of an allocated block. + unsafe { HeapReAlloc(heap, 0, ptr as c::LPVOID, new_size) as *mut u8 } } else { - realloc_fallback(self, ptr, layout, new_size) + // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will + // correctly handle `ptr` and return a pointer satisfying the guarantees of `System` + unsafe { realloc_fallback(self, ptr, layout, new_size) } } } } diff --git a/library/std/src/sys/windows/alloc/tests.rs b/library/std/src/sys/windows/alloc/tests.rs new file mode 100644 index 00000000000..674a3e1d92d --- /dev/null +++ b/library/std/src/sys/windows/alloc/tests.rs @@ -0,0 +1,9 @@ +use super::{Header, MIN_ALIGN}; +use crate::mem; + +#[test] +fn alloc_header() { + // Header must fit in the padding before an aligned pointer + assert!(mem::size_of::<Header>() <= MIN_ALIGN); + assert!(mem::align_of::<Header>() <= MIN_ALIGN); +} diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 9789ed085e2..3e4176ef7f8 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -285,8 +285,6 @@ pub const FD_SETSIZE: usize = 64; pub const STACK_SIZE_PARAM_IS_A_RESERVATION: DWORD = 0x00010000; -pub const HEAP_ZERO_MEMORY: DWORD = 0x00000008; - pub const STATUS_SUCCESS: NTSTATUS = 0x00000000; #[repr(C)] @@ -1017,11 +1015,6 @@ extern "system" { timeout: *const timeval, ) -> c_int; - pub fn GetProcessHeap() -> HANDLE; - pub fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; - pub fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID; - pub fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; - // >= Vista / Server 2008 // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw pub fn CreateSymbolicLinkW( |
