From 15094b2118cafe2980259dc3d6b05195a6bc3e99 Mon Sep 17 00:00:00 2001 From: HeroicKatora Date: Tue, 28 Jan 2020 11:57:16 +0100 Subject: [PATCH 1/3] Add AllocRef, similar to working group trait --- alloc-traits/src/helper.rs | 5 ++ alloc-traits/src/lib.rs | 1 + alloc-traits/src/ref_.rs | 107 +++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 alloc-traits/src/helper.rs create mode 100644 alloc-traits/src/ref_.rs diff --git a/alloc-traits/src/helper.rs b/alloc-traits/src/helper.rs new file mode 100644 index 0000000..0fdc2a4 --- /dev/null +++ b/alloc-traits/src/helper.rs @@ -0,0 +1,5 @@ +pub mod default_helper { + pub mod local { + + } +} diff --git a/alloc-traits/src/lib.rs b/alloc-traits/src/lib.rs index 009fecb..76753bb 100644 --- a/alloc-traits/src/lib.rs +++ b/alloc-traits/src/lib.rs @@ -21,6 +21,7 @@ mod layout; mod local; +mod ref_; pub mod util; pub use crate::layout::{Layout, NonZeroLayout}; diff --git a/alloc-traits/src/ref_.rs b/alloc-traits/src/ref_.rs new file mode 100644 index 0000000..8019a31 --- /dev/null +++ b/alloc-traits/src/ref_.rs @@ -0,0 +1,107 @@ +use core::ptr::{copy_nonoverlapping, write_bytes, NonNull}; + +use crate::NonZeroLayout; +use crate::LocalAlloc; + +/// An allocation without tracked lifetime. +pub struct Allocation { + ptr: NonNull, + layout: NonZeroLayout, +} + +/// A references to an allocator. +/// +/// Allocations must be live for the lifetime of `self`. That is one must be able to move the +/// `self` and keep all allocations. In particular, a reference to a [`LocalAlloc`] is an +/// `AllocRef`. +/// +/// [`LocalAlloc`]: trait.LocalAlloc.html +pub unsafe trait AllocRef { + /// Allocate one block of memory. + /// + /// The callee guarantees that a successful return contains a pointer that is valid for **at + /// least** the layout requested by the caller. + fn alloc(&mut self, layout: NonZeroLayout) -> Option; + + /// Deallocate a block previously allocated. + /// # Safety + /// The caller must ensure that: + /// * `alloc` has been previously returned from a call to `alloc`. + /// * There are no more pointer to the allocation. + unsafe fn dealloc(&mut self, alloc: Allocation); + + /// Allocate a block of memory initialized with zeros. + /// + /// The callee guarantees that a successful return contains a pointer that is valid for **at + /// least** the layout requested by the caller and the contiguous region of bytes, starting at + /// the pointer and with the size of the returned layout, is initialized and zeroed. + fn alloc_zeroed(&mut self, layout: NonZeroLayout) + -> Option + { + let allocation = self.alloc(layout)?; + unsafe { + write_bytes(allocation.ptr.as_ptr(), 0u8, allocation.layout.size().into()); + } + Some(allocation) + } + + /// Change the layout of a block previously allocated. + /// + /// The callee guarantees that a successful return contains a pointer that is valid for **at + /// least** the layout requested by the caller and the contiguous region of bytes, starting at + /// the pointer and with the size of the returned layout, is initialized with the prefix of the + /// previous allocation that is still valid. + unsafe fn realloc(&mut self, alloc: Allocation, layout: NonZeroLayout) + -> Option + { + let new_alloc = self.alloc(layout)?; + copy_nonoverlapping( + alloc.ptr.as_ptr(), + new_alloc.ptr.as_ptr(), + layout.size().min(alloc.layout.size()).into()); + Some(new_alloc) + } +} + +impl Allocation { + pub(crate) fn from_local(alloc: crate::local::Allocation) -> Self { + Allocation { + ptr: alloc.ptr, + layout: alloc.layout, + } + } + + pub(crate) fn into_local<'lt>(self) -> crate::local::Allocation<'lt> { + crate::local::Allocation { + ptr: self.ptr, + layout: self.layout, + lifetime: crate::AllocTime::default(), + } + } +} + +/// Note the +unsafe impl<'rf, T> AllocRef for &'rf T + where T: LocalAlloc<'rf> +{ + fn alloc(&mut self, layout: NonZeroLayout) -> Option { + LocalAlloc::<'rf>::alloc(*self, layout).map(Allocation::from_local) + } + + unsafe fn dealloc(&mut self, alloc: Allocation) { + LocalAlloc::<'rf>::dealloc(*self, alloc.into_local()) + } + + fn alloc_zeroed(&mut self, layout: NonZeroLayout) + -> Option + { + LocalAlloc::<'rf>::alloc_zeroed(*self, layout).map(Allocation::from_local) + } + + unsafe fn realloc(&mut self, alloc: Allocation, layout: NonZeroLayout) + -> Option + { + LocalAlloc::<'rf>::realloc(*self, alloc.into_local(), layout) + .map(Allocation::from_local) + } +} From 8e0c15d2e2c973f6cf1361a358d1ef66e7afe192 Mon Sep 17 00:00:00 2001 From: Andreas Molzer Date: Tue, 28 Jan 2020 17:12:52 +0100 Subject: [PATCH 2/3] Expose the new trait and Allocation --- alloc-traits/src/lib.rs | 6 +++++- alloc-traits/src/ref_.rs | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/alloc-traits/src/lib.rs b/alloc-traits/src/lib.rs index 76753bb..35af6a1 100644 --- a/alloc-traits/src/lib.rs +++ b/alloc-traits/src/lib.rs @@ -25,6 +25,10 @@ mod ref_; pub mod util; pub use crate::layout::{Layout, NonZeroLayout}; -pub use crate::local::{AllocTime, Allocation, LocalAlloc}; +pub use crate::local::{AllocTime, LocalAlloc}; +#[deprecated="Use the name `LocalAllocation` instead"] +pub use crate::local::Allocation; +pub use crate::local::Allocation as LocalAllocation; #[allow(deprecated)] pub use crate::local::Invariant; +pub use crate::ref_::{AllocRef, Allocation as RefAllocation}; diff --git a/alloc-traits/src/ref_.rs b/alloc-traits/src/ref_.rs index 8019a31..99f3426 100644 --- a/alloc-traits/src/ref_.rs +++ b/alloc-traits/src/ref_.rs @@ -80,7 +80,12 @@ impl Allocation { } } -/// Note the +/// A reference is an `AllocRef`. +/// +/// Note the lifetime of the references is exactly the same as the lifetime of the trait. If the +/// reference outlived the parameter then the returned allocations would not live long enough. +/// Conversely, if the parameter outlived the lifetime then it would not be possible to construct +/// the reference necessary for the trait. unsafe impl<'rf, T> AllocRef for &'rf T where T: LocalAlloc<'rf> { From b449df93694910647119c32f7768b23dcbb19819 Mon Sep 17 00:00:00 2001 From: HeroicKatora Date: Thu, 30 Jan 2020 10:05:17 +0100 Subject: [PATCH 3/3] Consider the allocator stored in an allocation Avoiding the pointer to the allocator in every structure that contains a non-global allocator is possible by storing the pointer inside scratch space of existing allocations. However, that requires being passed one. --- alloc-traits/src/ref_.rs | 112 +++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 34 deletions(-) diff --git a/alloc-traits/src/ref_.rs b/alloc-traits/src/ref_.rs index 99f3426..5c6ceac 100644 --- a/alloc-traits/src/ref_.rs +++ b/alloc-traits/src/ref_.rs @@ -4,6 +4,7 @@ use crate::NonZeroLayout; use crate::LocalAlloc; /// An allocation without tracked lifetime. +#[derive(Copy, Clone)] pub struct Allocation { ptr: NonNull, layout: NonZeroLayout, @@ -11,58 +12,89 @@ pub struct Allocation { /// A references to an allocator. /// -/// Allocations must be live for the lifetime of `self`. That is one must be able to move the -/// `self` and keep all allocations. In particular, a reference to a [`LocalAlloc`] is an -/// `AllocRef`. +/// Allocations created from this instance are valid for the lifetime of `self`. That is one must +/// be able to move the `self` and all allocations remain valid. In particular, a reference to a +/// [`LocalAlloc`] is an `AllocRef` as it is an immutable pin. +/// +/// An allocation is said to be live when it is valid and has not been passed to `dealloc` and has +/// not been passed to `realloc` that returned `Some(_)`. +/// +/// Note that all methods require being passed some existing allocation, even if they do not +/// consume it. See `InitialAllocRef` for those that do not. /// /// [`LocalAlloc`]: trait.LocalAlloc.html pub unsafe trait AllocRef { /// Allocate one block of memory. /// - /// The callee guarantees that a successful return contains a pointer that is valid for **at - /// least** the layout requested by the caller. - fn alloc(&mut self, layout: NonZeroLayout) -> Option; - - /// Deallocate a block previously allocated. - /// # Safety - /// The caller must ensure that: - /// * `alloc` has been previously returned from a call to `alloc`. - /// * There are no more pointer to the allocation. - unsafe fn dealloc(&mut self, alloc: Allocation); + /// The `existing` allocation can be used by the `AllocRef` to uniquely identify the allocator + /// that is used internaly. It must be valid for the same `AllocRef`. + /// + /// The callee guarantees that a successful return contains a pointer that fits the layout + /// requested by the caller and the layout in the struct is the same as requested. + unsafe fn alloc_from(&mut self, existing: Allocation, layout: NonZeroLayout) -> Option; /// Allocate a block of memory initialized with zeros. /// - /// The callee guarantees that a successful return contains a pointer that is valid for **at - /// least** the layout requested by the caller and the contiguous region of bytes, starting at - /// the pointer and with the size of the returned layout, is initialized and zeroed. - fn alloc_zeroed(&mut self, layout: NonZeroLayout) + /// The callee guarantees that a successful return contains a pointer that fits the layout + /// requested by the caller and the layout in the struct is the same as requested. + unsafe fn alloc_zeroed_from(&mut self, existing: Allocation, layout: NonZeroLayout) -> Option { - let allocation = self.alloc(layout)?; - unsafe { - write_bytes(allocation.ptr.as_ptr(), 0u8, allocation.layout.size().into()); - } + let allocation = self.alloc_from(existing, layout)?; + write_bytes(allocation.ptr.as_ptr(), 0u8, allocation.layout.size().into()); Some(allocation) } + /// Deallocate a block previously allocated. + /// # Safety + /// The caller must ensure that: + /// * `alloc` has been previously returned from a call to `alloc`. + /// * There are no more pointer to the allocation. + unsafe fn dealloc(&mut self, alloc: Allocation); + /// Change the layout of a block previously allocated. /// - /// The callee guarantees that a successful return contains a pointer that is valid for **at - /// least** the layout requested by the caller and the contiguous region of bytes, starting at - /// the pointer and with the size of the returned layout, is initialized with the prefix of the - /// previous allocation that is still valid. + /// The callee guarantees that a successful return contains a pointer that it fits the layout + /// requested by the caller and the contiguous region of bytes, starting at the pointer and + /// with the size of the returned layout, is initialized with the prefix of the previous + /// allocation that is still valid, and that the layout in the returned struct is the same as + /// requested. unsafe fn realloc(&mut self, alloc: Allocation, layout: NonZeroLayout) -> Option { - let new_alloc = self.alloc(layout)?; + let new_alloc = self.alloc_from(alloc, layout)?; copy_nonoverlapping( alloc.ptr.as_ptr(), new_alloc.ptr.as_ptr(), layout.size().min(alloc.layout.size()).into()); + self.dealloc(alloc); Some(new_alloc) } } +/// A trait for an `AllocRef` that requires no existing allocation to allocate. +pub unsafe trait InitialAllocRef: AllocRef { + /// Allocate one block of memory. + /// + /// The callee guarantees that a successful return contains a pointer that fits the layout + /// requested by the caller and the layout in the struct is the same as requested. + fn alloc(&mut self, layout: NonZeroLayout) -> Option; + + /// Allocate a block of memory initialized with zeros. + /// + /// The callee guarantees that a successful return contains a pointer that fits the layout + /// requested by the caller and the layout in the struct is the same as requested. + fn alloc_zeroed(&mut self, layout: NonZeroLayout) + -> Option + { + let allocation = InitialAllocRef::alloc(self, layout)?; + unsafe { + write_bytes(allocation.ptr.as_ptr(), 0u8, allocation.layout.size().into()); + } + Some(allocation) + } +} + impl Allocation { pub(crate) fn from_local(alloc: crate::local::Allocation) -> Self { Allocation { @@ -89,18 +121,18 @@ impl Allocation { unsafe impl<'rf, T> AllocRef for &'rf T where T: LocalAlloc<'rf> { - fn alloc(&mut self, layout: NonZeroLayout) -> Option { - LocalAlloc::<'rf>::alloc(*self, layout).map(Allocation::from_local) + unsafe fn alloc_from(&mut self, _: Allocation, layout: NonZeroLayout) -> Option { + InitialAllocRef::alloc(self, layout) } - unsafe fn dealloc(&mut self, alloc: Allocation) { - LocalAlloc::<'rf>::dealloc(*self, alloc.into_local()) - } - - fn alloc_zeroed(&mut self, layout: NonZeroLayout) + unsafe fn alloc_zeroed_from(&mut self, _: Allocation, layout: NonZeroLayout) -> Option { - LocalAlloc::<'rf>::alloc_zeroed(*self, layout).map(Allocation::from_local) + InitialAllocRef::alloc_zeroed(self, layout) + } + + unsafe fn dealloc(&mut self, alloc: Allocation) { + LocalAlloc::<'rf>::dealloc(*self, alloc.into_local()) } unsafe fn realloc(&mut self, alloc: Allocation, layout: NonZeroLayout) @@ -110,3 +142,15 @@ unsafe impl<'rf, T> AllocRef for &'rf T .map(Allocation::from_local) } } + +unsafe impl<'rf, T> InitialAllocRef for &'rf T + where T: LocalAlloc<'rf> +{ + fn alloc(&mut self, layout: NonZeroLayout) -> Option { + LocalAlloc::<'rf>::alloc(*self, layout).map(Allocation::from_local) + } + + fn alloc_zeroed(&mut self, layout: NonZeroLayout) -> Option { + LocalAlloc::<'rf>::alloc_zeroed(*self, layout).map(Allocation::from_local) + } +}