libshit

Just some random shit
git clone https://git.neptards.moe/neptards/libshit.git
Log | Files | Refs | Submodules | README | LICENSE

polymorphic_stack.cpp (5129B)


      1 #include "libshit/container/polymorphic_stack.hpp"
      2 
      3 #include "libshit/doctest.hpp"
      4 #include "libshit/utils.hpp"
      5 
      6 #include <tracy/Tracy.hpp>
      7 
      8 #include <limits>
      9 #include <vector>
     10 
     11 #if !LIBSHIT_OS_IS_WINDOWS && !LIBSHIT_OS_IS_VITA
     12 #  include <unistd.h>
     13 #  include <sys/mman.h>
     14 #endif
     15 
     16 namespace Libshit
     17 {
     18   using namespace PolymorphicStackDetail;
     19   TEST_SUITE_BEGIN("Libshit::PolymorphicStack");
     20 
     21   std::pair<void*, std::uint32_t> MallocAllocator::Alloc(
     22     std::uint32_t size) noexcept
     23   {
     24     auto ptr = std::malloc(size);
     25     TracyAllocS(ptr, size, 5);
     26     return { ptr, size };
     27   }
     28 
     29   void MallocAllocator::Free(void* ptr, std::uint32_t size) noexcept
     30   {
     31     TracyFreeS(ptr, 5);
     32     std::free(ptr);
     33   }
     34 
     35 #if !LIBSHIT_OS_IS_WINDOWS && !LIBSHIT_OS_IS_VITA
     36 
     37   static const std::size_t PAGE_SIZE_M1 = sysconf(_SC_PAGE_SIZE) - 1;
     38 
     39   std::pair<void*, std::uint32_t> MmapAllocator::Alloc(
     40     std::uint32_t size) noexcept
     41   {
     42     if (std::numeric_limits<std::uint32_t>::max() - size < PAGE_SIZE_M1)
     43       return { nullptr, 0 };
     44 
     45     size = (size + PAGE_SIZE_M1) & ~PAGE_SIZE_M1;
     46     auto ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE,
     47                     MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     48     if (ptr == MAP_FAILED) return { nullptr, 0 };
     49 
     50     TracyAllocS(ptr, size, 5);
     51     return { ptr, size };
     52   }
     53 
     54   void MmapAllocator::Free(void* ptr, std::uint32_t size) noexcept
     55   {
     56     TracyFreeS(ptr, 5);
     57     munmap(ptr, size);
     58   }
     59 
     60 #endif
     61 
     62   static bool disable_mremap [[maybe_unused]] = false;
     63 #if LIBSHIT_OS_IS_LINUX
     64   std::uint32_t MmapAllocator::Extend(
     65     void* ptr, std::uint32_t old_size, std::uint32_t min_size,
     66     std::uint32_t max_size) noexcept
     67   {
     68     LIBSHIT_ASSERT(max_size >= min_size);
     69     if (disable_mremap) return 0;
     70     if (std::numeric_limits<std::uint32_t>::max() - max_size < PAGE_SIZE_M1)
     71       return 0;
     72 
     73     max_size = (max_size + PAGE_SIZE_M1) & ~PAGE_SIZE_M1;
     74     if (mremap(ptr, old_size, max_size, 0) != MAP_FAILED) return max_size;
     75     min_size = (min_size + PAGE_SIZE_M1) & ~PAGE_SIZE_M1;
     76     if (mremap(ptr, old_size, min_size, 0) != MAP_FAILED) return min_size;
     77     return 0;
     78   }
     79 #endif
     80 
     81   namespace
     82   {
     83     struct Base { virtual ~Base() = default; };
     84     template <std::size_t N>
     85     struct X : Base { char x[N]; };
     86   }
     87 
     88   TEST_CASE_TEMPLATE_DEFINE("PolymorphicStack", T, polymorphic_stack)
     89   {
     90     T stack;
     91     CHECK(stack.Size() == 0);
     92     CHECK(stack.Empty());
     93 
     94     SUBCASE("simple push/pop")
     95     {
     96       auto& x = stack.template Push<Base>();
     97       CHECK(stack.Size() == 1);
     98       CHECK(!stack.Empty());
     99       CHECK(&stack.Back() == &x);
    100 
    101       stack.Pop();
    102       CHECK(stack.Size() == 0);
    103       CHECK(stack.Empty());
    104     }
    105 
    106     SUBCASE("push multiple")
    107     {
    108       auto& a = stack.template Push<Base>();
    109       auto& b = stack.template Push<X<16>>();
    110       CHECK(reinterpret_cast<uintptr_t>(&b) ==
    111             reinterpret_cast<uintptr_t>(&a) + sizeof(Base) + sizeof(Head));
    112       auto& c = stack.template Push<X<32>>();
    113       CHECK(reinterpret_cast<uintptr_t>(&c) ==
    114             reinterpret_cast<uintptr_t>(&b) + sizeof(X<16>) + sizeof(Head));
    115 
    116       CHECK(&stack.Back() == &c); CHECK(stack.Size() == 3); stack.Pop();
    117       CHECK(&stack.Back() == &b); CHECK(stack.Size() == 2); stack.Pop();
    118       CHECK(&stack.Back() == &a); CHECK(stack.Size() == 1); stack.Pop();
    119       CHECK(stack.Size() == 0); CHECK(stack.Empty());
    120     }
    121 
    122     SUBCASE("push until overflow")
    123     {
    124       disable_mremap = true;
    125       AtScopeExit x{[]() { disable_mremap = false; }};
    126 
    127       auto& first = stack.template Push<X<128>>();
    128       std::vector<X<128>*> vec{&first};
    129       do
    130       {
    131         REQUIRE(vec.size() < 100'000); // do not eat all of the memory
    132         vec.push_back(&stack.template Push<X<128>>());
    133       }
    134       while (reinterpret_cast<char*>(vec.back()) ==
    135              reinterpret_cast<char*>(&first) +
    136                (sizeof(Head) + sizeof(X<128>)) * (vec.size()-1));
    137       CCAPTURE(vec.size());
    138 
    139       std::size_t n = vec.size();
    140       for (auto it = vec.rbegin(); it != vec.rend(); ++it)
    141       {
    142         CHECK(stack.Size() == n);
    143         CHECK(stack.Empty() == !n);
    144         --n;
    145         CHECK(&stack.Back() == *it);
    146         stack.Pop();
    147       }
    148 
    149       SUBCASE("overflow head")
    150       {
    151         auto& foo = stack.template Push<X<65536>>();
    152         CHECK(&stack.Back() == &foo);
    153         CHECK(stack.Size() == 1);
    154       }
    155       SUBCASE("overflow tail")
    156       {
    157         auto& head = stack.template Push<Base>();
    158         auto& foo = stack.template Push<X<65536>>();
    159         CHECK(&stack.Back() == &foo);
    160         CHECK(stack.Size() == 2);
    161         stack.Pop();
    162         CHECK(&stack.Back() == &head);
    163       }
    164     }
    165   }
    166 
    167   TEST_CASE_TEMPLATE_INVOKE(
    168     polymorphic_stack, PolymorphicStack<Base, MallocAllocator>);
    169 #if !LIBSHIT_OS_IS_WINDOWS && !LIBSHIT_OS_IS_VITA
    170   TEST_CASE_TEMPLATE_INVOKE(
    171     polymorphic_stack, PolymorphicStack<Base, MmapAllocator>);
    172 #endif
    173 
    174   TEST_SUITE_END();
    175 }
    176 
    177 TYPE_TO_STRING(Libshit::PolymorphicStack<Libshit::Base, Libshit::MallocAllocator>);
    178 #if !LIBSHIT_OS_IS_WINDOWS && !LIBSHIT_OS_IS_VITA
    179 TYPE_TO_STRING(Libshit::PolymorphicStack<Libshit::Base, Libshit::MmapAllocator>);
    180 #endif