This is an automated email from the ASF dual-hosted git repository.

bneradt pushed a commit to branch dev-1-0-9
in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git

commit d6fbf554738960766c0861baf2da962abb2ab80d
Author: Alan M. Carroll <[email protected]>
AuthorDate: Sun Nov 10 15:14:22 2019 -0600

    Add FixedArena.
---
 swoc++/include/swoc/MemArena.h | 128 ++++++++++++++++++++++++++++-------------
 unit_tests/test_MemArena.cc    |  31 ++++++++++
 2 files changed, 118 insertions(+), 41 deletions(-)

diff --git a/swoc++/include/swoc/MemArena.h b/swoc++/include/swoc/MemArena.h
index 7e35a1f..2b2e3b0 100644
--- a/swoc++/include/swoc/MemArena.h
+++ b/swoc++/include/swoc/MemArena.h
@@ -22,6 +22,7 @@
 #include <mutex>
 #include <memory>
 #include <utility>
+#include <new>
 
 #include "swoc/MemSpan.h"
 #include "swoc/Scalar.h"
@@ -329,55 +330,89 @@ protected:
   // marks the last block to check. This keeps the set of blocks to check 
short.
 };
 
+/** Arena of a specific type on top of a @c MemArena.
+ *
+ * @tparam T Type in the arena.
+ *
+ * A pool of unused / free instances of @a T is kept for reuse. If none are 
available then a new
+ * instance is allocated from the arena.
+ */
+template <typename T> class FixedArena
+{
+  using self_type = FixedArena; ///< Self reference type.
+protected:
+  /// Rebinding type for instances on the free list.
+  struct Item {
+    Item *_next; ///< Next item in the free list.
+  };
+
+  Item _list { nullptr }; ///< List of dead instances.
+  MemArena &_arena; ///< Memory source.
+
+public:
+  /** Construct a pool.
+   *
+   * @param arena The arena for memory.
+   */
+  explicit FixedArena(MemArena &arena);
+
+  /** Create a new instance.
+   *
+   * @tparam Args Constructor argument types.
+   * @param args Constructor arguments.
+   * @return A new instance of @a T.
+   */
+  template <typename... Args> T *make(Args... args);
+
+  /** Destroy an instance.
+   *
+   * @param t The instance to destroy.
+   *
+   * The instance is destructed and then put on the free list for re-use.
+   */
+  void destroy(T *t);
+};
 // Implementation
 
-inline auto
-MemArena::Block::Linkage::next_ptr(Block *b) -> Block *&
+inline auto MemArena::Block::Linkage::next_ptr(Block *b) -> Block *&
 {
   return b->_link._next;
 }
 
-inline auto
-MemArena::Block::Linkage::prev_ptr(Block *b) -> Block *&
+inline auto MemArena::Block::Linkage::prev_ptr(Block *b) -> Block *&
 {
   return b->_link._prev;
 }
 
 inline MemArena::Block::Block(size_t n) : size(n) {}
 
-inline char *
-MemArena::Block::data()
+inline char *MemArena::Block::data()
 {
   return reinterpret_cast<char *>(this + 1);
 }
 
-inline const char *
-MemArena::Block::data() const
+inline const char *MemArena::Block::data() const
 {
   return reinterpret_cast<const char *>(this + 1);
 }
 
-inline bool
-MemArena::Block::contains(const void *ptr) const
+inline bool MemArena::Block::contains(const void *ptr) const
 {
   const char *base = this->data();
   return base <= ptr && ptr < base + size;
 }
 
-inline size_t
-MemArena::Block::remaining() const
+inline size_t MemArena::Block::remaining() const
 {
   return size - allocated;
 }
 
-inline bool
-MemArena::Block::is_full() const
+inline bool MemArena::Block::is_full() const
 {
   return this->remaining() < MIN_FREE_SPACE;
 }
 
-inline MemSpan<void>
-MemArena::Block::alloc(size_t n)
+inline MemSpan<void> MemArena::Block::alloc(size_t n)
 {
   if (n > this->remaining()) {
     throw(std::invalid_argument{"MemArena::Block::alloc size is more than 
remaining."});
@@ -387,80 +422,91 @@ MemArena::Block::alloc(size_t n)
   return zret;
 }
 
-template <typename T, typename... Args>
-T *
-MemArena::make(Args &&... args)
+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()
+inline MemSpan<void> MemArena::Block::remnant()
 {
   return {this->data() + allocated, this->remaining()};
 }
 
-inline MemArena::Block &
-MemArena::Block::discard()
+inline MemArena::Block &MemArena::Block::discard()
 {
   allocated = 0;
   return *this;
 }
 
-inline size_t
-MemArena::size() const
+inline size_t MemArena::size() const
 {
   return _active_allocated;
 }
 
-inline size_t
-MemArena::allocated_size() const
+inline size_t MemArena::allocated_size() const
 {
   return _frozen_allocated + _active_allocated;
 }
 
-inline size_t
-MemArena::remaining() const
+inline size_t MemArena::remaining() const
 {
   return _active.empty() ? 0 : _active.head()->remaining();
 }
 
-inline MemSpan<void>
-MemArena::remnant()
+inline MemSpan<void> MemArena::remnant()
 {
   return _active.empty() ? MemSpan<void>() : _active.head()->remnant();
 }
 
-inline size_t
-MemArena::reserved_size() const
+inline size_t MemArena::reserved_size() const
 {
   return _active_reserved + _frozen_reserved;
 }
 
-inline auto
-MemArena::begin() const -> const_iterator
+inline auto MemArena::begin() const -> const_iterator
 {
   return _active.begin();
 }
 
-inline auto
-MemArena::end() const -> const_iterator
+inline auto MemArena::end() const -> const_iterator
 {
   return _active.end();
 }
 
-inline auto
-MemArena::frozen_begin() const -> const_iterator
+inline auto MemArena::frozen_begin() const -> const_iterator
 {
   return _frozen.begin();
 }
 
-inline auto
-MemArena::frozen_end() const -> const_iterator
+inline auto MemArena::frozen_end() const -> const_iterator
 {
   return _frozen.end();
 }
 
+template <typename T> FixedArena<T>::FixedArena(MemArena &arena) : 
_arena(arena) {
+  static_assert(sizeof(T) >= sizeof(T*));
+}
+
+template <typename T> template <typename... Args> T 
*FixedArena<T>::make(Args... args)
+{
+  if (_list._next) {
+    void* t = _list._next;
+    _list._next = _list._next->_next;
+    return new (t) T(std::forward(args)...);
+  }
+  return _arena.template make<T>(std::forward(args)...);
+}
+
+template <typename T> void FixedArena<T>::destroy(T *t)
+{
+  if (t) {
+    t->~T(); // destructor.
+    auto item   = reinterpret_cast<Item *>(t);
+    item->_next = _list._next;
+    _list._next = item;
+  }
+}
+
 } // namespace swoc
diff --git a/unit_tests/test_MemArena.cc b/unit_tests/test_MemArena.cc
index 9a76fc7..a3d6ace 100644
--- a/unit_tests/test_MemArena.cc
+++ b/unit_tests/test_MemArena.cc
@@ -26,6 +26,7 @@
 
 using swoc::MemSpan;
 using swoc::MemArena;
+using swoc::FixedArena;
 using std::string_view;
 using swoc::TextView;
 using namespace std::literals;
@@ -358,3 +359,33 @@ TEST_CASE("MemArena temporary", "[libswoc][MemArena][tmp]")
   }
   REQUIRE(arena.reserved_size() == rsize);
 }
+
+TEST_CASE("FixedArena", "[libswoc][FixedArena]") {
+  struct Thing {
+    int x = 0;
+    std::string name;
+  };
+  MemArena arena;
+  FixedArena<Thing> fa{arena};
+
+  Thing * one = fa.make();
+  Thing * two = fa.make();
+  two->x = 17;
+  two->name = "Bob";
+  fa.destroy(two);
+  Thing * three = fa.make();
+  REQUIRE(three == two); // reused instance.
+  REQUIRE(three->x == 0); // but reconstructed.
+  REQUIRE(three->name.empty() == true);
+  fa.destroy(three);
+  std::array<Thing*, 17> things;
+  for ( auto & ptr : things ) {
+    ptr = fa.make();
+  }
+  two = things[things.size() - 1];
+  for ( auto & ptr : things) {
+    fa.destroy(ptr);
+  }
+  three = fa.make();
+  REQUIRE(two == three);
+};

Reply via email to