Skip to content

Commit bf00fd8

Browse files
committed
Auto merge of #50738 - gnzlbg:exc, r=<try>
Add missing _excess methods to Alloc and use them in RawVec Adds the following methods to the `Alloc` trait: * `alloc_zeroed_excess` * `shrink_in_place_excess` * `grow_in_place_excess` Uses them in the appropriate places within the `RawVec` implementation, with the exception of `shrink_in_place_excess` which should be used in `RawVec::shrink_to_fit`, but which will be done in a different PR.
2 parents 2a3f536 + 6e2fcbd commit bf00fd8

File tree

3 files changed

+168
-24
lines changed

3 files changed

+168
-24
lines changed

src/liballoc/raw_vec.rs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use core::ops::Drop;
1414
use core::ptr::{self, NonNull, Unique};
1515
use core::slice;
1616

17-
use alloc::{Alloc, Layout, Global, oom};
17+
use alloc::{Alloc, Layout, Global, oom, Excess};
1818
use alloc::CollectionAllocErr;
1919
use alloc::CollectionAllocErr::*;
2020
use boxed::Box;
@@ -92,17 +92,17 @@ impl<T, A: Alloc> RawVec<T, A> {
9292
alloc_guard(alloc_size).unwrap_or_else(|_| capacity_overflow());
9393

9494
// handles ZSTs and `cap = 0` alike
95-
let ptr = if alloc_size == 0 {
96-
NonNull::<T>::dangling().as_opaque()
95+
let (ptr, cap) = if alloc_size == 0 {
96+
(NonNull::<T>::dangling().as_opaque(), cap)
9797
} else {
9898
let align = mem::align_of::<T>();
9999
let result = if zeroed {
100-
a.alloc_zeroed(Layout::from_size_align(alloc_size, align).unwrap())
100+
a.alloc_zeroed_excess(Layout::from_size_align(alloc_size, align).unwrap())
101101
} else {
102-
a.alloc(Layout::from_size_align(alloc_size, align).unwrap())
102+
a.alloc_excess(Layout::from_size_align(alloc_size, align).unwrap())
103103
};
104104
match result {
105-
Ok(ptr) => ptr,
105+
Ok(Excess(ptr, alloc_size)) => (ptr, alloc_size / elem_size),
106106
Err(_) => oom(),
107107
}
108108
};
@@ -407,16 +407,17 @@ impl<T, A: Alloc> RawVec<T, A> {
407407

408408
alloc_guard(new_layout.size())?;
409409

410-
let res = match self.current_layout() {
410+
let Excess(ptr, alloc_size) = match self.current_layout() {
411411
Some(layout) => {
412412
debug_assert!(new_layout.align() == layout.align());
413-
self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size())
413+
self.a.realloc_excess(NonNull::from(self.ptr).as_opaque(),
414+
layout, new_layout.size())
414415
}
415-
None => self.a.alloc(new_layout),
416-
};
416+
None => self.a.alloc_excess(new_layout),
417+
}?;
417418

418-
self.ptr = res?.cast().into();
419-
self.cap = new_cap;
419+
self.ptr = ptr.cast().into();
420+
self.cap = alloc_size / mem::size_of::<T>();
420421

421422
Ok(())
422423
}
@@ -485,16 +486,16 @@ impl<T, A: Alloc> RawVec<T, A> {
485486
// FIXME: may crash and burn on over-reserve
486487
alloc_guard(new_layout.size())?;
487488

488-
let res = match self.current_layout() {
489+
let Excess(ptr, alloc_size) = match self.current_layout() {
489490
Some(layout) => {
490491
debug_assert!(new_layout.align() == layout.align());
491-
self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size())
492+
self.a.realloc_excess(NonNull::from(self.ptr).as_opaque(),
493+
layout, new_layout.size())
492494
}
493-
None => self.a.alloc(new_layout),
494-
};
495-
496-
self.ptr = res?.cast().into();
497-
self.cap = new_cap;
495+
None => self.a.alloc_excess(new_layout),
496+
}?;
497+
self.ptr = ptr.cast().into();
498+
self.cap = alloc_size / mem::size_of::<T>();
498499

499500
Ok(())
500501
}
@@ -604,11 +605,11 @@ impl<T, A: Alloc> RawVec<T, A> {
604605
let new_layout = Layout::new::<T>().repeat(new_cap).unwrap().0;
605606
// FIXME: may crash and burn on over-reserve
606607
alloc_guard(new_layout.size()).unwrap_or_else(|_| capacity_overflow());
607-
match self.a.grow_in_place(
608+
match self.a.grow_in_place_excess(
608609
NonNull::from(self.ptr).as_opaque(), old_layout, new_layout.size(),
609610
) {
610-
Ok(_) => {
611-
self.cap = new_cap;
611+
Ok(Excess(_, alloc_size)) => {
612+
self.cap = alloc_size / mem::size_of::<T>();
612613
true
613614
}
614615
Err(_) => {
@@ -666,10 +667,13 @@ impl<T, A: Alloc> RawVec<T, A> {
666667
let new_size = elem_size * amount;
667668
let align = mem::align_of::<T>();
668669
let old_layout = Layout::from_size_align_unchecked(old_size, align);
669-
match self.a.realloc(NonNull::from(self.ptr).as_opaque(),
670+
match self.a.realloc_excess(NonNull::from(self.ptr).as_opaque(),
670671
old_layout,
671672
new_size) {
672-
Ok(p) => self.ptr = p.cast().into(),
673+
Ok(Excess(ptr, alloc_size)) => {
674+
self.ptr = ptr.cast().into();
675+
self.cap = alloc_size / mem::size_of::<T>();
676+
},
673677
Err(_) => oom(),
674678
}
675679
}

src/liballoc/vec.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,10 @@ impl<T> Vec<T> {
574574
/// It will drop down as close as possible to the length but the allocator
575575
/// may still inform the vector that there is space for a few more elements.
576576
///
577+
/// Note: `shrink_to_fit` is a non-binding request. Whether the `Vec` size
578+
/// will be shrunk at all, and if so by how much depends on the particular
579+
/// allocator being used.
580+
///
577581
/// # Examples
578582
///
579583
/// ```
@@ -598,6 +602,10 @@ impl<T> Vec<T> {
598602
/// Panics if the current capacity is smaller than the supplied
599603
/// minimum capacity.
600604
///
605+
/// Note: `shrink_to` is a non-binding request. Whether the `Vec` size
606+
/// will be shrunk at all, and if so by how much depends on the particular
607+
/// allocator being used.
608+
///
601609
/// # Examples
602610
///
603611
/// ```

src/libcore/alloc.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,28 @@ pub unsafe trait Alloc {
794794
.map(|p| Excess(p, usable_size.1))
795795
}
796796

797+
/// Behaves like `alloc`, but also ensures that the contents are set to zero
798+
/// before being returned. For some `layout` inputs, like arrays, this may
799+
/// include extra storage usable for additional data.
800+
///
801+
/// # Safety
802+
///
803+
/// This function is unsafe for the same reasons that `alloc` is.
804+
///
805+
/// # Errors
806+
///
807+
/// Returning `Err` indicates that either memory is exhausted or
808+
/// `layout` does not meet allocator's size or alignment
809+
/// constraints, just as in `alloc`.
810+
///
811+
/// Clients wishing to abort computation in response to an
812+
/// allocation error are encouraged to call the allocator's `oom`
813+
/// method, rather than directly invoking `panic!` or similar.
814+
unsafe fn alloc_zeroed_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
815+
let usable_size = self.usable_size(&layout);
816+
self.alloc_zeroed(layout).map(|p| Excess(p, usable_size.1))
817+
}
818+
797819
/// Attempts to extend the allocation referenced by `ptr` to fit `new_size`.
798820
///
799821
/// If this returns `Ok`, then the allocator has asserted that the
@@ -845,6 +867,60 @@ pub unsafe trait Alloc {
845867
}
846868
}
847869

870+
/// Attempts to extend the allocation referenced by `ptr` to fit `new_size`.
871+
/// For some `layout` inputs, like arrays, this may include extra storage
872+
/// usable for additional data.
873+
///
874+
/// If this returns `Ok`, then the allocator has asserted that the
875+
/// memory block referenced by `ptr` now fits `new_size`, and thus can
876+
/// be used to carry data of a layout of that size and same alignment as
877+
/// `layout`. (The allocator is allowed to
878+
/// expend effort to accomplish this, such as extending the memory block to
879+
/// include successor blocks, or virtual memory tricks.)
880+
///
881+
/// Regardless of what this method returns, ownership of the
882+
/// memory block referenced by `ptr` has not been transferred, and
883+
/// the contents of the memory block are unaltered.
884+
///
885+
/// # Safety
886+
///
887+
/// This function is unsafe because undefined behavior can result
888+
/// if the caller does not ensure all of the following:
889+
///
890+
/// * `ptr` must be currently allocated via this allocator,
891+
///
892+
/// * `layout` must *fit* the `ptr` (see above); note the
893+
/// `new_size` argument need not fit it,
894+
///
895+
/// * `new_size` must not be less than `layout.size()`,
896+
///
897+
/// # Errors
898+
///
899+
/// Returns `Err(CannotReallocInPlace)` when the allocator is
900+
/// unable to assert that the memory block referenced by `ptr`
901+
/// could fit `layout`.
902+
///
903+
/// Note that one cannot pass `CannotReallocInPlace` to the `oom`
904+
/// method; clients are expected either to be able to recover from
905+
/// `grow_in_place` failures without aborting, or to fall back on
906+
/// another reallocation method before resorting to an abort.
907+
unsafe fn grow_in_place_excess(&mut self,
908+
ptr: NonNull<Opaque>,
909+
layout: Layout,
910+
new_size: usize) -> Result<Excess, CannotReallocInPlace> {
911+
let _ = ptr; // this default implementation doesn't care about the actual address.
912+
debug_assert!(new_size >= layout.size);
913+
let (_l, u) = self.usable_size(&layout);
914+
// _l <= layout.size() [guaranteed by usable_size()]
915+
// layout.size() <= new_layout.size() [required by this method]
916+
if new_size <= u {
917+
return Ok(Excess(ptr, u));
918+
} else {
919+
return Err(CannotReallocInPlace);
920+
}
921+
}
922+
923+
848924
/// Attempts to shrink the allocation referenced by `ptr` to fit `new_size`.
849925
///
850926
/// If this returns `Ok`, then the allocator has asserted that the
@@ -900,6 +976,62 @@ pub unsafe trait Alloc {
900976
}
901977
}
902978

979+
/// Attempts to shrink the allocation referenced by `ptr` to fit `new_size`.
980+
/// For some `layout` inputs, like arrays, this may include extra storage
981+
/// usable for additional data.
982+
///
983+
/// If this returns `Ok`, then the allocator has asserted that the
984+
/// memory block referenced by `ptr` now fits `new_size`, and
985+
/// thus can only be used to carry data of that smaller
986+
/// layout. (The allocator is allowed to take advantage of this,
987+
/// carving off portions of the block for reuse elsewhere.) The
988+
/// truncated contents of the block within the smaller layout are
989+
/// unaltered, and ownership of block has not been transferred.
990+
///
991+
/// If this returns `Err`, then the memory block is considered to
992+
/// still represent the original (larger) `layout`. None of the
993+
/// block has been carved off for reuse elsewhere, ownership of
994+
/// the memory block has not been transferred, and the contents of
995+
/// the memory block are unaltered.
996+
///
997+
/// # Safety
998+
///
999+
/// This function is unsafe because undefined behavior can result
1000+
/// if the caller does not ensure all of the following:
1001+
///
1002+
/// * `ptr` must be currently allocated via this allocator,
1003+
///
1004+
/// * `layout` must *fit* the `ptr` (see above); note the
1005+
/// `new_size` argument need not fit it,
1006+
///
1007+
/// * `new_size` must not be greater than `layout.size()`
1008+
/// (and must be greater than zero),
1009+
///
1010+
/// # Errors
1011+
///
1012+
/// Returns `Err(CannotReallocInPlace)` when the allocator is
1013+
/// unable to assert that the memory block referenced by `ptr`
1014+
/// could fit `layout`.
1015+
///
1016+
/// Note that one cannot pass `CannotReallocInPlace` to the `oom`
1017+
/// method; clients are expected either to be able to recover from
1018+
/// `shrink_in_place` failures without aborting, or to fall back
1019+
/// on another reallocation method before resorting to an abort.
1020+
unsafe fn shrink_in_place_excess(&mut self,
1021+
ptr: NonNull<Opaque>,
1022+
layout: Layout,
1023+
new_size: usize) -> Result<Excess, CannotReallocInPlace> {
1024+
let _ = ptr; // this default implementation doesn't care about the actual address.
1025+
debug_assert!(new_size <= layout.size);
1026+
let (l, u) = self.usable_size(&layout);
1027+
// layout.size() <= _u [guaranteed by usable_size()]
1028+
// new_layout.size() <= layout.size() [required by this method]
1029+
if l <= new_size {
1030+
return Ok(Excess(ptr, u));
1031+
} else {
1032+
return Err(CannotReallocInPlace);
1033+
}
1034+
}
9031035

9041036
// == COMMON USAGE PATTERNS ==
9051037
// alloc_one, dealloc_one, alloc_array, realloc_array. dealloc_array

0 commit comments

Comments
 (0)