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]")

Reply via email to