Skip to content

Commit be97d13

Browse files
committed
Auto merge of #75657 - TimDiekmann:allocref-cleanup, r=Amanieu
Clean up AllocRef implementation and documentation r? @Amanieu
2 parents b287b56 + 63d241a commit be97d13

File tree

3 files changed

+183
-178
lines changed

3 files changed

+183
-178
lines changed

library/alloc/src/alloc.rs

+74-74
Original file line numberDiff line numberDiff line change
@@ -161,30 +161,69 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 {
161161
unsafe { __rust_alloc_zeroed(layout.size(), layout.align()) }
162162
}
163163

164+
impl Global {
165+
#[inline]
166+
fn alloc_impl(&mut self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocErr> {
167+
match layout.size() {
168+
0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)),
169+
// SAFETY: `layout` is non-zero in size,
170+
size => unsafe {
171+
let raw_ptr = if zeroed { alloc_zeroed(layout) } else { alloc(layout) };
172+
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
173+
Ok(NonNull::slice_from_raw_parts(ptr, size))
174+
},
175+
}
176+
}
177+
178+
// Safety: Same as `AllocRef::grow`
179+
#[inline]
180+
unsafe fn grow_impl(
181+
&mut self,
182+
ptr: NonNull<u8>,
183+
layout: Layout,
184+
new_size: usize,
185+
zeroed: bool,
186+
) -> Result<NonNull<[u8]>, AllocErr> {
187+
debug_assert!(
188+
new_size >= layout.size(),
189+
"`new_size` must be greater than or equal to `layout.size()`"
190+
);
191+
192+
match layout.size() {
193+
// SAFETY: the caller must ensure that the `new_size` does not overflow.
194+
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
195+
0 => unsafe {
196+
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
197+
self.alloc_impl(new_layout, zeroed)
198+
},
199+
200+
// SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size`
201+
// as required by safety conditions. Other conditions must be upheld by the caller
202+
old_size => unsafe {
203+
// `realloc` probably checks for `new_size >= size` or something similar.
204+
intrinsics::assume(new_size >= layout.size());
205+
206+
let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
207+
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
208+
if zeroed {
209+
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
210+
}
211+
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
212+
},
213+
}
214+
}
215+
}
216+
164217
#[unstable(feature = "allocator_api", issue = "32838")]
165218
unsafe impl AllocRef for Global {
166219
#[inline]
167220
fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
168-
let size = layout.size();
169-
let ptr = if size == 0 {
170-
layout.dangling()
171-
} else {
172-
// SAFETY: `layout` is non-zero in size,
173-
unsafe { NonNull::new(alloc(layout)).ok_or(AllocErr)? }
174-
};
175-
Ok(NonNull::slice_from_raw_parts(ptr, size))
221+
self.alloc_impl(layout, false)
176222
}
177223

178224
#[inline]
179225
fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
180-
let size = layout.size();
181-
let ptr = if size == 0 {
182-
layout.dangling()
183-
} else {
184-
// SAFETY: `layout` is non-zero in size,
185-
unsafe { NonNull::new(alloc_zeroed(layout)).ok_or(AllocErr)? }
186-
};
187-
Ok(NonNull::slice_from_raw_parts(ptr, size))
226+
self.alloc_impl(layout, true)
188227
}
189228

190229
#[inline]
@@ -203,26 +242,8 @@ unsafe impl AllocRef for Global {
203242
layout: Layout,
204243
new_size: usize,
205244
) -> Result<NonNull<[u8]>, AllocErr> {
206-
debug_assert!(
207-
new_size >= layout.size(),
208-
"`new_size` must be greater than or equal to `layout.size()`"
209-
);
210-
211-
// SAFETY: `new_size` must be non-zero, which is checked in the match expression.
212-
// If `new_size` is zero, then `old_size` has to be zero as well.
213-
// Other conditions must be upheld by the caller
214-
unsafe {
215-
match layout.size() {
216-
0 => self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())),
217-
old_size => {
218-
// `realloc` probably checks for `new_size >= size` or something similar.
219-
intrinsics::assume(new_size >= old_size);
220-
let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
221-
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
222-
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
223-
}
224-
}
225-
}
245+
// SAFETY: all conditions must be upheld by the caller
246+
unsafe { self.grow_impl(ptr, layout, new_size, false) }
226247
}
227248

228249
#[inline]
@@ -232,27 +253,8 @@ unsafe impl AllocRef for Global {
232253
layout: Layout,
233254
new_size: usize,
234255
) -> Result<NonNull<[u8]>, AllocErr> {
235-
debug_assert!(
236-
new_size >= layout.size(),
237-
"`new_size` must be greater than or equal to `layout.size()`"
238-
);
239-
240-
// SAFETY: `new_size` must be non-zero, which is checked in the match expression.
241-
// If `new_size` is zero, then `old_size` has to be zero as well.
242-
// Other conditions must be upheld by the caller
243-
unsafe {
244-
match layout.size() {
245-
0 => self.alloc_zeroed(Layout::from_size_align_unchecked(new_size, layout.align())),
246-
old_size => {
247-
// `realloc` probably checks for `new_size >= size` or something similar.
248-
intrinsics::assume(new_size >= old_size);
249-
let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
250-
raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
251-
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
252-
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
253-
}
254-
}
255-
}
256+
// SAFETY: all conditions must be upheld by the caller
257+
unsafe { self.grow_impl(ptr, layout, new_size, true) }
256258
}
257259

258260
#[inline]
@@ -262,30 +264,28 @@ unsafe impl AllocRef for Global {
262264
layout: Layout,
263265
new_size: usize,
264266
) -> Result<NonNull<[u8]>, AllocErr> {
265-
let old_size = layout.size();
266267
debug_assert!(
267-
new_size <= old_size,
268+
new_size <= layout.size(),
268269
"`new_size` must be smaller than or equal to `layout.size()`"
269270
);
270271

271-
let ptr = if new_size == 0 {
272+
match new_size {
272273
// SAFETY: conditions must be upheld by the caller
273-
unsafe {
274+
0 => unsafe {
274275
self.dealloc(ptr, layout);
275-
}
276-
layout.dangling()
277-
} else {
278-
// SAFETY: new_size is not zero,
279-
// Other conditions must be upheld by the caller
280-
let raw_ptr = unsafe {
281-
// `realloc` probably checks for `new_size <= old_size` or something similar.
282-
intrinsics::assume(new_size <= old_size);
283-
realloc(ptr.as_ptr(), layout, new_size)
284-
};
285-
NonNull::new(raw_ptr).ok_or(AllocErr)?
286-
};
276+
Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0))
277+
},
287278

288-
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
279+
// SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
280+
new_size => unsafe {
281+
// `realloc` probably checks for `new_size <= size` or something similar.
282+
intrinsics::assume(new_size <= layout.size());
283+
284+
let raw_ptr = realloc(ptr.as_ptr(), layout, new_size);
285+
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
286+
Ok(NonNull::slice_from_raw_parts(ptr, new_size))
287+
},
288+
}
289289
}
290290
}
291291

library/core/src/alloc/mod.rs

+29-29
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ pub unsafe trait AllocRef {
151151
/// alignment and a size given by `new_size`. To accomplish this, the allocator may extend the
152152
/// allocation referenced by `ptr` to fit the new layout.
153153
///
154+
/// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
155+
/// transferred to this allocator. The memory may or may not have been freed, and should be
156+
/// considered unusable unless it was transferred back to the caller again via the return value
157+
/// of this method.
158+
///
154159
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
155160
/// this allocator, and the contents of the memory block are unaltered.
156161
///
@@ -192,12 +197,9 @@ pub unsafe trait AllocRef {
192197
"`new_size` must be greater than or equal to `layout.size()`"
193198
);
194199

195-
let new_layout =
196-
// SAFETY: the caller must ensure that the `new_size` does not overflow.
197-
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
198-
// The caller must ensure that `new_size` is greater than or equal to zero. If it's equal
199-
// to zero, it's catched beforehand.
200-
unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
200+
// SAFETY: the caller must ensure that the `new_size` does not overflow.
201+
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
202+
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
201203
let new_ptr = self.alloc(new_layout)?;
202204

203205
// SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
@@ -206,10 +208,11 @@ pub unsafe trait AllocRef {
206208
// `copy_nonoverlapping` is safe.
207209
// The safety contract for `dealloc` must be upheld by the caller.
208210
unsafe {
209-
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_non_null_ptr().as_ptr(), size);
211+
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size);
210212
self.dealloc(ptr, layout);
211-
Ok(new_ptr)
212213
}
214+
215+
Ok(new_ptr)
213216
}
214217

215218
/// Behaves like `grow`, but also ensures that the new contents are set to zero before being
@@ -218,12 +221,12 @@ pub unsafe trait AllocRef {
218221
/// The memory block will contain the following contents after a successful call to
219222
/// `grow_zeroed`:
220223
/// * Bytes `0..layout.size()` are preserved from the original allocation.
221-
/// * Bytes `layout.size()..old_size` will either be preserved or zeroed,
222-
/// depending on the allocator implementation. `old_size` refers to the size of
223-
/// the `MemoryBlock` prior to the `grow_zeroed` call, which may be larger than the size
224-
/// that was originally requested when it was allocated.
225-
/// * Bytes `old_size..new_size` are zeroed. `new_size` refers to
226-
/// the size of the `MemoryBlock` returned by the `grow` call.
224+
/// * Bytes `layout.size()..old_size` will either be preserved or zeroed, depending on the
225+
/// allocator implementation. `old_size` refers to the size of the memory block prior to
226+
/// the `grow_zeroed` call, which may be larger than the size that was originally requested
227+
/// when it was allocated.
228+
/// * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory
229+
/// block returned by the `grow` call.
227230
///
228231
/// # Safety
229232
///
@@ -261,12 +264,9 @@ pub unsafe trait AllocRef {
261264
"`new_size` must be greater than or equal to `layout.size()`"
262265
);
263266

264-
let new_layout =
265-
// SAFETY: the caller must ensure that the `new_size` does not overflow.
266-
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
267-
// The caller must ensure that `new_size` is greater than or equal to zero. If it's equal
268-
// to zero, it's caught beforehand.
269-
unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
267+
// SAFETY: the caller must ensure that the `new_size` does not overflow.
268+
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
269+
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
270270
let new_ptr = self.alloc_zeroed(new_layout)?;
271271

272272
// SAFETY: because `new_size` must be greater than or equal to `size`, both the old and new
@@ -275,10 +275,11 @@ pub unsafe trait AllocRef {
275275
// `copy_nonoverlapping` is safe.
276276
// The safety contract for `dealloc` must be upheld by the caller.
277277
unsafe {
278-
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_non_null_ptr().as_ptr(), size);
278+
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size);
279279
self.dealloc(ptr, layout);
280-
Ok(new_ptr)
281280
}
281+
282+
Ok(new_ptr)
282283
}
283284

284285
/// Attempts to shrink the memory block.
@@ -290,8 +291,8 @@ pub unsafe trait AllocRef {
290291
///
291292
/// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
292293
/// transferred to this allocator. The memory may or may not have been freed, and should be
293-
/// considered unusable unless it was transferred back to the caller again via the
294-
/// return value of this method.
294+
/// considered unusable unless it was transferred back to the caller again via the return value
295+
/// of this method.
295296
///
296297
/// If this method returns `Err`, then ownership of the memory block has not been transferred to
297298
/// this allocator, and the contents of the memory block are unaltered.
@@ -332,11 +333,9 @@ pub unsafe trait AllocRef {
332333
"`new_size` must be smaller than or equal to `layout.size()`"
333334
);
334335

335-
let new_layout =
336336
// SAFETY: the caller must ensure that the `new_size` does not overflow.
337337
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid for a Layout.
338-
// The caller must ensure that `new_size` is greater than zero.
339-
unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
338+
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
340339
let new_ptr = self.alloc(new_layout)?;
341340

342341
// SAFETY: because `new_size` must be lower than or equal to `size`, both the old and new
@@ -345,10 +344,11 @@ pub unsafe trait AllocRef {
345344
// `copy_nonoverlapping` is safe.
346345
// The safety contract for `dealloc` must be upheld by the caller.
347346
unsafe {
348-
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_non_null_ptr().as_ptr(), size);
347+
ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), size);
349348
self.dealloc(ptr, layout);
350-
Ok(new_ptr)
351349
}
350+
351+
Ok(new_ptr)
352352
}
353353

354354
/// Creates a "by reference" adaptor for this instance of `AllocRef`.

0 commit comments

Comments
 (0)