This is an automated email from the ASF dual-hosted git repository. bneradt pushed a commit to branch mem-align in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git
commit 1412f2992af1dc462164343de5d4ea0569e79079 Author: Alan M. Carroll <[email protected]> AuthorDate: Tue Jun 21 13:53:51 2022 -0500 Add alignment support to MemSpan, MemArena. --- code/include/swoc/MemArena.h | 84 +++++++++++++++++++++++++++++++++++--------- code/include/swoc/MemSpan.h | 40 ++++++++++++++++++--- code/include/swoc/Vectray.h | 2 +- unit_tests/test_MemSpan.cc | 15 ++++++++ 4 files changed, 119 insertions(+), 22 deletions(-) diff --git a/code/include/swoc/MemArena.h b/code/include/swoc/MemArena.h index 0800d3e..ab7a39e 100644 --- a/code/include/swoc/MemArena.h +++ b/code/include/swoc/MemArena.h @@ -310,9 +310,44 @@ public: /// @return The amount of free space. size_t remaining() const; + /** Get aligned and sized remnant. + * + * @tparam T Element type. + * @param n Number of instances of @a T + * @return A span that is in the remnant, correctly aligned with minimal padding. + * + * This is guaranteed to be the same bytes as if @c alloc<T> was called. The returned span will + * always be the specified size, the remnant will be expanded as needed. + */ + template <typename T> MemSpan<T> remnant_span(size_t n); + + /** Get remnant memory. + * + * @param n Memory size in bytes. + * @return The remnant memory. + * + * This differs from similar methods in that + * - the memory is not aligned + * - the remnant is forced to be sufficiently large + * + * Generally not the best choice, but useful when writing other templated methods on top of + * @c MemArena by handling the edge case of @c void. + */ + template <> MemSpan<void> remnant_span<void>(size_t n); + /// @return Contiguous free space in the current internal block. MemSpan<void> remnant(); + /** Get an aligned remnant. + * + * @param n Remnant size. + * @param align Memory alignment (default 1, must be power of 2). + * @return Space in the remnant with minimal alignment padding. + * + * @note This will always return a span of @a n bytes, the remnant will be expanded as needed. + */ + MemSpan<void> remnant(size_t n, size_t align = DEFAULT_ALIGNMENT); + /** Require @a n bytes of contiguous memory to be available for allocation. * * @param n Number of bytes. @@ -351,7 +386,6 @@ public: const_iterator frozen_begin() const; const_iterator frozen_end() const; - protected: /** Internally allocates a new block of memory of size @a n bytes. * @@ -509,20 +543,6 @@ MemArena::Block::alloc(size_t n, size_t align) { return zret; } -template <typename T> -MemSpan<T> -MemArena::alloc_span(size_t n) { - return this->alloc(sizeof(T) * n, size_t{alignof(T)}).rebind<T>(); -} - -template <typename T, typename... Args> -T * -MemArena::make(Args &&... args) { - return new (this->alloc(sizeof(T)).data()) T(std::forward<Args>(args)...); -} - -inline MemArena::MemArena(size_t n) : _reserve_hint(n) {} - inline MemSpan<void> MemArena::Block::remnant() { return {this->data() + allocated, this->remaining()}; @@ -545,10 +565,40 @@ MemArena::Block::operator delete([[maybe_unused]] void *ptr, void *place) noexce inline size_t MemArena::Block::align_padding(void const *ptr, size_t align) { - auto delta = uintptr_t(ptr) & (size_t(align) - 1); - return delta ? size_t(align) - delta : delta; + if (auto delta = uintptr_t(ptr) & (align - 1) ; delta > 0) { + return align - delta; + } + return 0; +} + +inline MemArena::MemArena(size_t n) : _reserve_hint(n) {} + +template <typename T> +MemSpan<T> +MemArena::alloc_span(size_t n) { + return this->alloc(sizeof(T) * n, alignof(T)).rebind<T>(); +} + +template <typename T, typename... Args> +T * +MemArena::make(Args &&... args) { + return new (this->alloc(sizeof(T), alignof(T)).data()) T(std::forward<Args>(args)...); +} + +template <typename T> +MemSpan<T> +MemArena::remnant_span(size_t n) { + auto span = this->require(sizeof(T) * n, alignof(T)).remnant(); + return span.remove_prefix(Block::align_padding(span.data(), alignof(T))).rebind<T>(); } +template <> +inline MemSpan<void> +MemArena::remnant_span<void>(size_t n) { return this->require(n).remnant().prefix(n); } + +inline MemSpan<void> +MemArena::remnant(size_t n, size_t align) { return this->require(n, align).remnant().prefix(n); } + inline size_t MemArena::size() const { return _active_allocated; diff --git a/code/include/swoc/MemSpan.h b/code/include/swoc/MemSpan.h index b51528a..d6f107a 100644 --- a/code/include/swoc/MemSpan.h +++ b/code/include/swoc/MemSpan.h @@ -21,6 +21,7 @@ #include <exception> #include "swoc/swoc_version.h" +#include "swoc/Scalar.h" namespace swoc { inline namespace SWOC_VERSION_NS { /** A span of contiguous piece of memory. @@ -442,7 +443,7 @@ public: @return An instance that contains the leading @a n bytes of @a this. */ - self_type prefix(size_t n) const; + constexpr self_type prefix(size_t n); /** Shrink the span by removing @a n leading bytes. * @@ -457,7 +458,7 @@ public: * @param n Number of bytes to retrieve. * @return An instance that contains the trailing @a count elements of @a this. */ - self_type suffix(size_t n) const; + constexpr self_type suffix(size_t n); /** Shrink the span by removing @a n bytes. * @@ -478,6 +479,26 @@ public: */ constexpr self_type subspan(size_t offset, size_t count) const; + /** Align span for a type. + * + * @tparam T Alignment type. + * @return A suffix of the span suitably aligned for @a T. + * + * The minimum amount of space is removed from the front to yield an aligned span. If the span is not large + * enough to perform the alignment, the pointer is aligned and the size reduced to zero (empty). + */ + template <typename T> self_type align() const; + + /** Force memory alignment. + * + * @param n Alignment size (must be power of 2). + * @return An aligned span. + * + * The minimum amount of space is removed from the front to yield an aligned span. If the span is not large + * enough to perform the alignment, the pointer is aligned and the size reduced to zero (empty). + */ + self_type align(size_t n) const; + /** Return a view of the memory. * * @return A @c string_view covering the span contents. @@ -953,7 +974,7 @@ MemSpan<void>::contains(value_type const *ptr) const { } inline MemSpan<void> -MemSpan<void>::prefix(size_t n) const { +constexpr MemSpan<void>::prefix(size_t n) { return {_ptr, std::min(n, _size)}; } @@ -966,7 +987,7 @@ MemSpan<void>::remove_prefix(size_t n) { } inline MemSpan<void> -MemSpan<void>::suffix(size_t count) const { +constexpr MemSpan<void>::suffix(size_t count) { count = std::min(count, _size); return {static_cast<char *>(this->data_end()) - count, count}; } @@ -977,6 +998,17 @@ MemSpan<void>::remove_suffix(size_t count) { return *this; } +template <typename T> +MemSpan<void>::self_type +MemSpan<void>::align() const { return this->align(alignof(T)); } + +inline MemSpan<void>::self_type +MemSpan<void>::align(size_t n) const { + auto p = uintptr_t(_ptr); + auto padding = p & (n - 1); + return { reinterpret_cast<void*>(p + padding), _size - std::min<uintptr_t>(_size, padding) }; +} + template <typename U> MemSpan<U> MemSpan<void>::rebind() const { diff --git a/code/include/swoc/Vectray.h b/code/include/swoc/Vectray.h index 38f67ec..1940625 100644 --- a/code/include/swoc/Vectray.h +++ b/code/include/swoc/Vectray.h @@ -261,7 +261,7 @@ template <typename T, size_t N, class A> Vectray<T, N, A>::FixedStore::~FixedSto template<typename T, size_t N, class A> MemSpan<T> Vectray<T, N, A>::FixedStore::span() { - return MemSpan(_raw).template rebind<T>(); + return MemSpan<std::byte>(_raw).template rebind<T>(); } template<typename T, size_t N, class A> diff --git a/unit_tests/test_MemSpan.cc b/unit_tests/test_MemSpan.cc index cee9c89..956ab0c 100644 --- a/unit_tests/test_MemSpan.cc +++ b/unit_tests/test_MemSpan.cc @@ -98,6 +98,21 @@ TEST_CASE("MemSpan<void>", "[libswoc][MemSpan]") REQUIRE(left.data_end() == span.data()); REQUIRE(left.size() + span.size() == 1024); + MemSpan<void> a(buff, sizeof(buff)); + MemSpan<void> b; + b = a.align<int>(); + REQUIRE(b.data() == a.data()); + REQUIRE(b.size() == a.size()); + + b = a.suffix(a.size() - 2).align<int>(); + REQUIRE(b.data() != a.data()); + REQUIRE(b.size() != a.size()); + auto i = a.rebind<int>(); + REQUIRE(b.data() == i.data() + 1); + + b = a.suffix(a.size() - 2).align(alignof(int)); + REQUIRE(b.data() == i.data() + 1); + REQUIRE(b.rebind<int>().count() == i.count() - 1); }; TEST_CASE("MemSpan conversions", "[libswoc][MemSpan]")
