Skip to content

Commit 4b556ce

Browse files
authored
Rollup merge of rust-lang#69027 - TimDiekmann:zeroed-alloc, r=Amanieu
Add missing `_zeroed` varants to `AllocRef` The majority of the allocator wg has decided to add the missing `_zeroed` variants to `AllocRef`: > these should be added since they can be efficiently implemented with the `mremap` system call on Linux. `mremap` allows you to move/grow/shrink a memory mapping, and any new pages added for growth are guaranteed to be zeroed. > > If `AllocRef` does not have these methods then the user will have to manually write zeroes to the added memory since the API makes no guarantees on their contents. For the full discussion please see rust-lang/wg-allocators#14. This PR provides default implementations for `realloc_zeroed`, `alloc_excess_zeroed`, `realloc_excess_zeroed`, and `grow_in_place_zeroed`. r? @Amanieu
2 parents c33d531 + 97d1f8d commit 4b556ce

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed

src/libcore/alloc.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,59 @@ pub unsafe trait AllocRef {
853853
result
854854
}
855855

856+
/// Behaves like `realloc`, but also ensures that the new contents
857+
/// are set to zero before being returned.
858+
///
859+
/// # Safety
860+
///
861+
/// This function is unsafe for the same reasons that `realloc` is.
862+
///
863+
/// # Errors
864+
///
865+
/// Returns `Err` only if the new layout
866+
/// does not meet the allocator's size
867+
/// and alignment constraints of the allocator, or if reallocation
868+
/// otherwise fails.
869+
///
870+
/// Implementations are encouraged to return `Err` on memory
871+
/// exhaustion rather than panicking or aborting, but this is not
872+
/// a strict requirement. (Specifically: it is *legal* to
873+
/// implement this trait atop an underlying native allocation
874+
/// library that aborts on memory exhaustion.)
875+
///
876+
/// Clients wishing to abort computation in response to a
877+
/// reallocation error are encouraged to call the [`handle_alloc_error`] function,
878+
/// rather than directly invoking `panic!` or similar.
879+
///
880+
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
881+
unsafe fn realloc_zeroed(
882+
&mut self,
883+
ptr: NonNull<u8>,
884+
layout: Layout,
885+
new_size: usize,
886+
) -> Result<NonNull<u8>, AllocErr> {
887+
let old_size = layout.size();
888+
889+
if new_size >= old_size {
890+
if let Ok(()) = self.grow_in_place_zeroed(ptr, layout, new_size) {
891+
return Ok(ptr);
892+
}
893+
} else if new_size < old_size {
894+
if let Ok(()) = self.shrink_in_place(ptr, layout, new_size) {
895+
return Ok(ptr);
896+
}
897+
}
898+
899+
// otherwise, fall back on alloc + copy + dealloc.
900+
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
901+
let result = self.alloc_zeroed(new_layout);
902+
if let Ok(new_ptr) = result {
903+
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), cmp::min(old_size, new_size));
904+
self.dealloc(ptr, layout);
905+
}
906+
result
907+
}
908+
856909
/// Behaves like `alloc`, but also ensures that the contents
857910
/// are set to zero before being returned.
858911
///
@@ -904,6 +957,31 @@ pub unsafe trait AllocRef {
904957
self.alloc(layout).map(|p| Excess(p, usable_size.1))
905958
}
906959

960+
/// Behaves like `alloc`, but also returns the whole size of
961+
/// the returned block. For some `layout` inputs, like arrays, this
962+
/// may include extra storage usable for additional data.
963+
/// Also it ensures that the contents are set to zero before being returned.
964+
///
965+
/// # Safety
966+
///
967+
/// This function is unsafe for the same reasons that `alloc` is.
968+
///
969+
/// # Errors
970+
///
971+
/// Returning `Err` indicates that either memory is exhausted or
972+
/// `layout` does not meet allocator's size or alignment
973+
/// constraints, just as in `alloc`.
974+
///
975+
/// Clients wishing to abort computation in response to an
976+
/// allocation error are encouraged to call the [`handle_alloc_error`] function,
977+
/// rather than directly invoking `panic!` or similar.
978+
///
979+
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
980+
unsafe fn alloc_excess_zeroed(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
981+
let usable_size = self.usable_size(&layout);
982+
self.alloc_zeroed(layout).map(|p| Excess(p, usable_size.1))
983+
}
984+
907985
/// Behaves like `realloc`, but also returns the whole size of
908986
/// the returned block. For some `layout` inputs, like arrays, this
909987
/// may include extra storage usable for additional data.
@@ -934,6 +1012,37 @@ pub unsafe trait AllocRef {
9341012
self.realloc(ptr, layout, new_size).map(|p| Excess(p, usable_size.1))
9351013
}
9361014

1015+
/// Behaves like `realloc`, but also returns the whole size of
1016+
/// the returned block. For some `layout` inputs, like arrays, this
1017+
/// may include extra storage usable for additional data.
1018+
/// Also it ensures that the contents are set to zero before being returned.
1019+
///
1020+
/// # Safety
1021+
///
1022+
/// This function is unsafe for the same reasons that `realloc` is.
1023+
///
1024+
/// # Errors
1025+
///
1026+
/// Returning `Err` indicates that either memory is exhausted or
1027+
/// `layout` does not meet allocator's size or alignment
1028+
/// constraints, just as in `realloc`.
1029+
///
1030+
/// Clients wishing to abort computation in response to a
1031+
/// reallocation error are encouraged to call the [`handle_alloc_error`] function,
1032+
/// rather than directly invoking `panic!` or similar.
1033+
///
1034+
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
1035+
unsafe fn realloc_excess_zeroed(
1036+
&mut self,
1037+
ptr: NonNull<u8>,
1038+
layout: Layout,
1039+
new_size: usize,
1040+
) -> Result<Excess, AllocErr> {
1041+
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
1042+
let usable_size = self.usable_size(&new_layout);
1043+
self.realloc_zeroed(ptr, layout, new_size).map(|p| Excess(p, usable_size.1))
1044+
}
1045+
9371046
/// Attempts to extend the allocation referenced by `ptr` to fit `new_size`.
9381047
///
9391048
/// If this returns `Ok`, then the allocator has asserted that the
@@ -983,6 +1092,34 @@ pub unsafe trait AllocRef {
9831092
if new_size <= u { Ok(()) } else { Err(CannotReallocInPlace) }
9841093
}
9851094

1095+
/// Behaves like `grow_in_place`, but also ensures that the new
1096+
/// contents are set to zero before being returned.
1097+
///
1098+
/// # Safety
1099+
///
1100+
/// This function is unsafe for the same reasons that `grow_in_place` is.
1101+
///
1102+
/// # Errors
1103+
///
1104+
/// Returns `Err(CannotReallocInPlace)` when the allocator is
1105+
/// unable to assert that the memory block referenced by `ptr`
1106+
/// could fit `layout`.
1107+
///
1108+
/// Note that one cannot pass `CannotReallocInPlace` to the `handle_alloc_error`
1109+
/// function; clients are expected either to be able to recover from
1110+
/// `grow_in_place` failures without aborting, or to fall back on
1111+
/// another reallocation method before resorting to an abort.
1112+
unsafe fn grow_in_place_zeroed(
1113+
&mut self,
1114+
ptr: NonNull<u8>,
1115+
layout: Layout,
1116+
new_size: usize,
1117+
) -> Result<(), CannotReallocInPlace> {
1118+
self.grow_in_place(ptr, layout, new_size)?;
1119+
ptr.as_ptr().add(layout.size()).write_bytes(0, new_size - layout.size());
1120+
Ok(())
1121+
}
1122+
9861123
/// Attempts to shrink the allocation referenced by `ptr` to fit `new_size`.
9871124
///
9881125
/// If this returns `Ok`, then the allocator has asserted that the

0 commit comments

Comments
 (0)