diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index c31874a..454e5c5 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -35,13 +35,18 @@ jobs: with: version: "5.0.0-beta1" - name: Install GCC - if: matrix.machine.os == 'ubuntu-latest' && matrix.machine.toolset == 'gcc' + if: matrix.machine.toolset == 'gcc' run: sudo apt-get update && sudo apt-get install -y gcc g++ + - name: Clang libstdc++ image workaround + if: matrix.machine.toolset == 'clang' + uses: mjp41/workaround8649@7929373c0fe5caf844d8115adccef39e3b5362e7 + with: + os: ${{ matrix.machine.os }} - name: Install Clang & LLVM - if: matrix.machine.os == 'ubuntu-latest' && matrix.machine.toolset == 'clang' + if: matrix.machine.toolset == 'clang' run: sudo apt-get update && sudo apt-get install -y clang llvm lld - name: Install msbuild to PATH - if: matrix.machine.os == 'windows-latest' && matrix.machine.toolset == 'msc' + if: matrix.machine.toolset == 'msc' uses: microsoft/setup-msbuild@v1.1 - name: Run premake run: premake5 ${{ matrix.machine.action }} --toolset=${{ matrix.machine.toolset }} --dialect=C++20 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f391d44..45b5019 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,13 +34,18 @@ jobs: with: version: "5.0.0-beta1" - name: Install GCC - if: matrix.machine.os == 'ubuntu-latest' && matrix.machine.toolset == 'gcc' + if: matrix.machine.toolset == 'gcc' run: sudo apt update && sudo apt install -y gcc g++ - name: Install Clang & LLVM - if: matrix.machine.os == 'ubuntu-latest' && matrix.machine.toolset == 'clang' + if: matrix.machine.toolset == 'clang' run: sudo apt update && sudo apt install -y clang llvm lld + - name: Clang libstdc++ image workaround + if: matrix.machine.toolset == 'clang' + uses: mjp41/workaround8649@7929373c0fe5caf844d8115adccef39e3b5362e7 + with: + os: ${{ matrix.machine.os }} - name: Install msbuild to PATH - if: matrix.machine.os == 'windows-latest' && matrix.machine.toolset == 'msc' + if: matrix.machine.toolset == 'msc' uses: microsoft/setup-msbuild@v1.1 - name: Run premake run: premake5 ${{ matrix.machine.action }} --toolset=${{ matrix.machine.toolset }} --dialect=${{ matrix.dialect }} diff --git a/include/ktl/allocators/linear_allocator_fwd.h b/include/ktl/allocators/linear_allocator_fwd.h index 89fd2bc..ad6f48d 100644 --- a/include/ktl/allocators/linear_allocator_fwd.h +++ b/include/ktl/allocators/linear_allocator_fwd.h @@ -1,5 +1,6 @@ #pragma once +#include "reference_fwd.h" #include "shared_fwd.h" #include "threaded_fwd.h" #include "type_allocator_fwd.h" @@ -18,6 +19,12 @@ namespace ktl template using type_linear_allocator = type_allocator>; + /** + * @brief Shorthand for a typed, weak-reference linear allocator + */ + template + using type_reference_linear_allocator = type_allocator>>; + /** * @brief Shorthand for a typed, ref-counted linear allocator */ diff --git a/include/ktl/allocators/reference.h b/include/ktl/allocators/reference.h new file mode 100644 index 0000000..35574a9 --- /dev/null +++ b/include/ktl/allocators/reference.h @@ -0,0 +1,124 @@ +#pragma once + +#include "../utility/aligned_malloc.h" +#include "../utility/alignment.h" +#include "../utility/empty_base.h" +#include "../utility/meta.h" +#include "reference_fwd.h" + +#include +#include + +namespace ktl +{ + template + class reference + { + private: + static_assert(detail::has_no_value_type_v, "Building on top of typed allocators is not allowed. Use allocators without a type"); + + public: + typedef typename detail::get_size_type_t size_type; + + /** + * @brief Constructor for forwarding any arguments to the underlying allocator + */ + explicit reference(Alloc& alloc) noexcept: + m_Alloc(&alloc) {} + + reference(const reference& other) noexcept : + m_Alloc(other.m_Alloc) {} + + reference(reference&& other) noexcept : + m_Alloc(other.m_Alloc) {} + + reference& operator=(const reference& rhs) noexcept + { + m_Alloc = rhs.m_Alloc; + + return *this; + } + + reference& operator=(reference&& rhs) noexcept + { + m_Alloc = rhs.m_Alloc; + + return *this; + } + + bool operator==(const reference& rhs) const + noexcept(detail::has_nothrow_equal_v) + { + return m_Alloc == rhs.m_Alloc && *m_Alloc == *rhs.m_Alloc; + } + + bool operator!=(const reference& rhs) const + noexcept(detail::has_nothrow_not_equal_v) + { + return m_Alloc != rhs.m_Alloc || *m_Alloc != *rhs.m_Alloc; + } + +#pragma region Allocation + void* allocate(size_t n) + noexcept(detail::has_nothrow_allocate_v) + { + return m_Alloc->allocate(n); + } + + void deallocate(void* p, size_t n) + noexcept(detail::has_nothrow_deallocate_v) + { + m_Alloc->deallocate(p, n); + } +#pragma endregion + +#pragma region Construction + template + typename std::enable_if, void>::type + construct(T* p, Args&&... args) + noexcept(detail::has_nothrow_construct_v) + { + m_Alloc->construct(p, std::forward(args)...); + } + + template + typename std::enable_if, void>::type + destroy(T* p) + noexcept(detail::has_nothrow_destroy_v) + { + m_Alloc->destroy(p); + } +#pragma endregion + +#pragma region Utility + template + typename std::enable_if, size_type>::type + max_size() const + noexcept(detail::has_nothrow_max_size_v) + { + return m_Alloc->max_size(); + } + + template + typename std::enable_if, bool>::type + owns(void* p) const + noexcept(detail::has_nothrow_owns_v) + { + return m_Alloc->owns(p); + } +#pragma endregion + + Alloc& get_allocator() noexcept + { + return *m_Alloc; + } + + const Alloc& get_allocator() const noexcept + { + return *m_Alloc; + } + + private: + Alloc* m_Alloc; + }; +} \ No newline at end of file diff --git a/include/ktl/allocators/reference_fwd.h b/include/ktl/allocators/reference_fwd.h new file mode 100644 index 0000000..fffa190 --- /dev/null +++ b/include/ktl/allocators/reference_fwd.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace ktl +{ + // Wrapper class for making allocator ref-counted + template + class reference; +} \ No newline at end of file diff --git a/include/ktl/allocators/segragator_fwd.h b/include/ktl/allocators/segragator_fwd.h index b548877..e92fad4 100644 --- a/include/ktl/allocators/segragator_fwd.h +++ b/include/ktl/allocators/segragator_fwd.h @@ -1,5 +1,6 @@ #pragma once +#include "reference_fwd.h" #include "shared_fwd.h" #include "threaded_fwd.h" #include "type_allocator_fwd.h" @@ -86,13 +87,19 @@ namespace ktl using type_segragator_allocator = type_allocator>; /** - * @brief Shorthand for a typed, ref-counted freelist allocator + * @brief Shorthand for a typed, weak-reference segragator allocator + */ + template + using type_reference_segragator_allocator = type_allocator>>; + + /** + * @brief Shorthand for a typed, ref-counted segragator allocator */ template using type_shared_segragator_allocator = type_allocator>>; /** - * @brief Shorthand for a typed, thread-safe, ref-counted freelist allocator + * @brief Shorthand for a typed, thread-safe, ref-counted segragator allocator */ template using type_threaded_segragator_allocator = type_allocator>>; diff --git a/include/ktl/containers/packed_ptr_fwd.h b/include/ktl/containers/packed_ptr_fwd.h index 22334c2..c409a3c 100644 --- a/include/ktl/containers/packed_ptr_fwd.h +++ b/include/ktl/containers/packed_ptr_fwd.h @@ -1,9 +1,10 @@ #pragma once #include +#include namespace ktl { - template + template)> class packed_ptr; } \ No newline at end of file diff --git a/include/ktl/ktl.h b/include/ktl/ktl.h index 026d419..2de6475 100644 --- a/include/ktl/ktl.h +++ b/include/ktl/ktl.h @@ -8,6 +8,7 @@ #include "allocators/mallocator.h" #include "allocators/null_allocator.h" #include "allocators/overflow.h" +#include "allocators/reference.h" #include "allocators/segragator.h" #include "allocators/shared.h" #include "allocators/stack_allocator.h" diff --git a/include/ktl/ktl_alloc_fwd.h b/include/ktl/ktl_alloc_fwd.h index c96bbbd..9cce92d 100644 --- a/include/ktl/ktl_alloc_fwd.h +++ b/include/ktl/ktl_alloc_fwd.h @@ -6,6 +6,7 @@ #include "allocators/linear_allocator_fwd.h" #include "allocators/mallocator_fwd.h" #include "allocators/overflow_fwd.h" +#include "allocators/reference_fwd.h" #include "allocators/segragator_fwd.h" #include "allocators/shared_fwd.h" #include "allocators/stack_allocator_fwd.h" diff --git a/src/shared/allocation_utility.h b/src/shared/allocation_utility.h index 39392e4..a3b5039 100644 --- a/src/shared/allocation_utility.h +++ b/src/shared/allocation_utility.h @@ -6,8 +6,57 @@ namespace ktl::test { + template + void assert_raw_allocate_deallocate(Alloc& allocator) + { + constexpr size_t amount = sizeof...(Is); + + size_t sizes[amount]{ Is... }; + void* ptrs[amount]{ nullptr }; + + std::shuffle(sizes, sizes + amount, random_generator); + + // Allocate all with random sizes + for (size_t i = 0; i < amount; i++) + { + void* valid_ptr = allocator.allocate(sizes[i]); + KTL_TEST_ASSERT(valid_ptr); + ptrs[i] = valid_ptr; + } + + // Assert that they are all unique + for (size_t i = 1; i < amount; i++) + { + bool ptr_not_equal = ptrs[i - 1] != ptrs[i]; + KTL_TEST_ASSERT(ptr_not_equal); + } + + // Deallocate the first half + for (size_t i = 0; i < amount / 2; i++) + allocator.deallocate(ptrs[i], sizes[i]); + + // Allocate the first half again + for (size_t i = 0; i < amount / 2; i++) + { + void* valid_ptr = allocator.allocate(sizes[i]); + KTL_TEST_ASSERT(valid_ptr); + ptrs[i] = valid_ptr; + } + + // Assert that they are all still unique + for (size_t i = 1; i < amount; i++) + { + bool ptr_not_equal = ptrs[i - 1] != ptrs[i]; + KTL_TEST_ASSERT(ptr_not_equal); + } + + // Deallocate everything + for (size_t i = 0; i < amount; i++) + allocator.deallocate(ptrs[i], sizes[i]); + } + template - static T* assert_allocate(Alloc& alloc, const T& value) + T* assert_allocate(Alloc& alloc, const T& value) { T* ptr = std::allocator_traits::allocate(alloc, 1); diff --git a/src/test/cascading_allocator_test.cpp b/src/test/cascading_allocator_test.cpp index c2737b8..6284caa 100644 --- a/src/test/cascading_allocator_test.cpp +++ b/src/test/cascading_allocator_test.cpp @@ -15,6 +15,12 @@ namespace ktl::test::cascading_allocator { + KTL_ADD_TEST(test_cascading_linear_raw_allocate) + { + cascading> alloc; + assert_raw_allocate_deallocate<2, 4, 8, 16, 24, 32>(alloc); + } + KTL_ADD_TEST(test_cascading_linear_unordered_double) { type_cascading_allocator> alloc; diff --git a/src/test/fallback_allocator_test.cpp b/src/test/fallback_allocator_test.cpp index 6d664f7..9650fe8 100644 --- a/src/test/fallback_allocator_test.cpp +++ b/src/test/fallback_allocator_test.cpp @@ -79,6 +79,14 @@ namespace ktl::test::fallback_allocator static_assert(!std::is_same_v, "The allocator types shouldn't match"); } + KTL_ADD_TEST(test_cascading_linear_raw_allocate) + { + stack<16> primaryStack; + stack<4096> fallbackStack; + fallback, stack_allocator<4096>> alloc(primaryStack, fallbackStack); + assert_raw_allocate_deallocate<2, 4, 8, 16, 32, 64>(alloc); + } + KTL_ADD_TEST(test_fallback_stack_stack_unordered_double) { stack<16> primaryStack; diff --git a/src/test/freelist_allocator_test.cpp b/src/test/freelist_allocator_test.cpp index afdbf80..4f86cea 100644 --- a/src/test/freelist_allocator_test.cpp +++ b/src/test/freelist_allocator_test.cpp @@ -14,6 +14,13 @@ namespace ktl::test::freelist_allocator { + KTL_ADD_TEST(test_freelist_stack_allocator_raw_allocate) + { + stack<4096> block; + freelist<0, 8, stack_allocator<4096>> alloc(block); + assert_raw_allocate_deallocate<1, 2, 4, 4, 8, 8>(alloc); + } + KTL_ADD_TEST(test_freelist_stack_allocator_unordered_double) { stack<4096> block; diff --git a/src/test/linear_allocator_test.cpp b/src/test/linear_allocator_test.cpp index 75e6c3d..6990652 100644 --- a/src/test/linear_allocator_test.cpp +++ b/src/test/linear_allocator_test.cpp @@ -17,6 +17,12 @@ namespace ktl::test::linear_allocator { + KTL_ADD_TEST(test_linear_raw_allocate) + { + ktl::linear_allocator<4096> alloc; + assert_raw_allocate_deallocate<2, 4, 8, 16, 32, 64>(alloc); + } + KTL_ADD_TEST(test_linear_allocator_unordered_double) { type_linear_allocator alloc; diff --git a/src/test/mallocator_test.cpp b/src/test/mallocator_test.cpp index 216ccc4..bccacf2 100644 --- a/src/test/mallocator_test.cpp +++ b/src/test/mallocator_test.cpp @@ -15,6 +15,12 @@ namespace ktl::test::mallocator { + KTL_ADD_TEST(test_mallocator_raw_allocate) + { + ktl::mallocator alloc; + assert_raw_allocate_deallocate<4, 8, 16, 32, 64, 128>(alloc); + } + KTL_ADD_TEST(test_mallocator_unordered_double) { type_mallocator alloc; diff --git a/src/test/overflow_allocator_test.cpp b/src/test/overflow_allocator_test.cpp index 401b0ab..2991070 100644 --- a/src/test/overflow_allocator_test.cpp +++ b/src/test/overflow_allocator_test.cpp @@ -41,6 +41,16 @@ namespace ktl::test::overflow_allocator KTL_TEST_ASSERT(stringBuffer.str().empty()); } + KTL_ADD_TEST(test_cascading_linear_raw_allocate) + { + assert_no_overflow([](std::ostream& stringOut) + { + stack<4096> block; + overflow> alloc(stringOut, block); + assert_raw_allocate_deallocate<2, 4, 8, 16, 32, 64>(alloc); + }); + } + KTL_ADD_TEST(test_overflow_stack_unordered_double) { assert_no_overflow([](std::ostream& stringOut) diff --git a/src/test/packed_ptr_test.cpp b/src/test/packed_ptr_test.cpp index 430a354..0fbc2b0 100644 --- a/src/test/packed_ptr_test.cpp +++ b/src/test/packed_ptr_test.cpp @@ -18,7 +18,7 @@ namespace ktl::test::packed_ptr int* in_ptr = &t; uint16_t in_value = 2; - ktl::packed_ptr pack(in_ptr, in_value); + ktl::packed_ptr pack(in_ptr, in_value); KTL_TEST_ASSERT(pack); KTL_TEST_ASSERT(in_ptr == pack.get_ptr()); diff --git a/src/test/reference_allocator_test.cpp b/src/test/reference_allocator_test.cpp new file mode 100644 index 0000000..627ff53 --- /dev/null +++ b/src/test/reference_allocator_test.cpp @@ -0,0 +1,71 @@ +#include "shared/allocation_utility.h" +#include "shared/test.h" +#include "shared/types.h" +#include "shared/vector_utility.h" + +#include "ktl/ktl_alloc_fwd.h" + +#define KTL_DEBUG_ASSERT +#include "ktl/allocators/linear_allocator.h" +#include "ktl/allocators/reference.h" +#include "ktl/allocators/type_allocator.h" + +#include + +// Naming scheme: test_reference_allocator_[Type] +// Contains tests that relate directly to the ktl::reference + +namespace ktl::test::reference_allocator +{ + KTL_ADD_TEST(test_reference_allocator_construct) + { + ktl::linear_allocator<4096> alloc; + + ktl::reference> ref(alloc); + + ktl::reference> moved_ref(std::move(ref)); + } + + KTL_ADD_TEST(test_reference_allocator_raw_allocate) + { + ktl::linear_allocator<4096> alloc; + + ktl::reference> ref(alloc); + + assert_raw_allocate_deallocate<2, 4, 8, 16, 32, 64>(ref); + } + +#pragma region std::vector + KTL_ADD_TEST(test_reference_allocator_std_vector_double) + { + ktl::linear_allocator<4096> alloc; + ktl::type_reference_linear_allocator ref_alloc(alloc); + std::vector> vec(ref_alloc); + assert_vector_values(vec); + } + + KTL_ADD_TEST(test_reference_allocator_std_vector_trivial) + { + ktl::linear_allocator<4096> alloc; + ktl::type_reference_linear_allocator ref_alloc(alloc); + std::vector> vec(ref_alloc); + assert_vector_values(vec); + } + + KTL_ADD_TEST(test_reference_allocator_std_vector_packed) + { + ktl::linear_allocator<4096> alloc; + ktl::type_reference_linear_allocator ref_alloc(alloc); + std::vector> vec(ref_alloc); + assert_vector_values(vec); + } + + KTL_ADD_TEST(test_reference_allocator_std_vector_complex) + { + ktl::linear_allocator<4096> alloc; + ktl::type_reference_linear_allocator ref_alloc(alloc); + std::vector> vec(ref_alloc); + assert_vector_values(vec); + } +#pragma endregion +} \ No newline at end of file diff --git a/src/test/segragator_allocator_test.cpp b/src/test/segragator_allocator_test.cpp index 804cb4a..fd5ff2e 100644 --- a/src/test/segragator_allocator_test.cpp +++ b/src/test/segragator_allocator_test.cpp @@ -78,16 +78,27 @@ namespace ktl::test::segragator_allocator Alloc4 alloc4; Alloc5 alloc5; - static_assert(detail::has_no_value_type::value); - static_assert(detail::has_no_value_type::value); - static_assert(detail::has_no_value_type::value); - static_assert(detail::has_no_value_type::value); - static_assert(detail::has_no_value_type::value); + static_assert(detail::has_no_value_type_v); + static_assert(detail::has_no_value_type_v); + static_assert(detail::has_no_value_type_v); + static_assert(detail::has_no_value_type_v); + static_assert(detail::has_no_value_type_v); static_assert(!std::is_same_v, "The allocator types shouldn't match"); static_assert(std::is_same_v, "The allocator types don't match"); static_assert(!std::is_same_v, "The allocator types shouldn't match"); } + + KTL_ADD_TEST(test_segragator_stack_stack_raw_allocate) + { + using Alloc = segragator<8, stack_allocator<1024>, stack_allocator<1024>>; + + stack<1024> primaryStack; + stack<1024> fallbackStack; + Alloc alloc(std::forward_as_tuple(primaryStack), std::forward_as_tuple(fallbackStack)); + + assert_raw_allocate_deallocate<2, 4, 8, 16, 32, 64>(alloc); + } KTL_ADD_TEST(test_segragator_stack_stack_unordered_double) { diff --git a/src/test/stack_allocator_test.cpp b/src/test/stack_allocator_test.cpp index 3e7f0a4..0c21e9c 100644 --- a/src/test/stack_allocator_test.cpp +++ b/src/test/stack_allocator_test.cpp @@ -18,6 +18,13 @@ namespace ktl::test::stack_allocator template using Alloc = ktl::type_stack_allocator; + KTL_ADD_TEST(test_stack_allocator_raw_allocate) + { + stack<4096> block; + ktl::stack_allocator<4096> alloc(block); + assert_raw_allocate_deallocate<2, 4, 8, 16, 32, 64>(alloc); + } + KTL_ADD_TEST(test_stack_allocator_unordered_double) { stack<4096> block;