diff --git a/include/ktl/containers/packed_ptr.h b/include/ktl/containers/packed_ptr.h new file mode 100644 index 0000000..7abb09f --- /dev/null +++ b/include/ktl/containers/packed_ptr.h @@ -0,0 +1,70 @@ +#pragma once + +#include "../utility/assert.h" +#include "../utility/bits.h" +#include "packed_ptr_fwd.h" + +#include + +namespace ktl +{ + /** + * @brief A packed pointer-like type which takes advantage of any bits that don't get used due to alignment + * @tparam PtrT The pointer type to use + */ + template + class packed_ptr + { + public: + static constexpr uintmax_t FREE_BITS = detail::log2(Alignment); + + private: + static_assert(Bits <= FREE_BITS, "The number of bits in use cannot surpass the number of free bits"); + + static constexpr uintptr_t INT_MASK = ((1ULL << Bits) - 1); + static constexpr uintptr_t PTR_MASK = ~((1ULL << FREE_BITS) - 1); + + public: + packed_ptr() noexcept : + m_Value(reinterpret_cast(nullptr)) {} + + template + explicit packed_ptr(PtrT p, Int value) noexcept : + m_Value((reinterpret_cast(p) & PTR_MASK) | (static_cast(value) & INT_MASK)) + { + // Pointer must be correctly aligned + KTL_ASSERT((reinterpret_cast(p) & (Alignment - 1)) == 0); + } + + operator bool() const noexcept { return m_Value; } + + PtrT get_ptr() const noexcept { return reinterpret_cast(m_Value & PTR_MASK); } + + template + Int get_int() const noexcept + { + static_assert(std::is_unsigned_v, "Packed integer must be unsigned"); + + return static_cast(m_Value & INT_MASK); + } + + void set_ptr(PtrT p) noexcept + { + // Pointer must be correctly aligned + KTL_ASSERT((reinterpret_cast(p) & (Alignment - 1)) == 0); + + m_Value = (reinterpret_cast(p) & PTR_MASK) | (m_Value & INT_MASK); + } + + template + void set_int(Int value) noexcept + { + static_assert(std::is_unsigned_v, "Packed integer must be unsigned"); + + m_Value = (m_Value & PTR_MASK) | (static_cast(value) & INT_MASK); + } + + private: + uintptr_t m_Value; + }; +} \ No newline at end of file diff --git a/include/ktl/containers/packed_ptr_fwd.h b/include/ktl/containers/packed_ptr_fwd.h new file mode 100644 index 0000000..22334c2 --- /dev/null +++ b/include/ktl/containers/packed_ptr_fwd.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace ktl +{ + template + class packed_ptr; +} \ No newline at end of file diff --git a/include/ktl/containers/trivial_vector.h b/include/ktl/containers/trivial_vector.h index 4858750..d194b60 100644 --- a/include/ktl/containers/trivial_vector.h +++ b/include/ktl/containers/trivial_vector.h @@ -403,13 +403,13 @@ namespace ktl /** * @brief Erases all elements in a range. - * @param first An iterator pointing to the element first. + * @param first An iterator pointing to the first element. * @param last An iterator pointing to the location after the last element. * @return An iterator pointing to the element immidiately after the erased ones. */ iterator erase(const_iterator first, const_iterator last) noexcept { - KTL_ASSERT(first >= last); + KTL_ASSERT(first <= last); KTL_ASSERT(first >= m_Begin && last <= m_End); std::memmove(const_cast(first), last, (m_End - last) * sizeof(T)); @@ -462,5 +462,7 @@ namespace ktl T* m_Begin; T* m_End; T* m_EndMax; + + //std::conditional_t m_Data; }; } \ No newline at end of file diff --git a/include/ktl/ktl.h b/include/ktl/ktl.h index 9f0d47d..026d419 100644 --- a/include/ktl/ktl.h +++ b/include/ktl/ktl.h @@ -16,6 +16,7 @@ // Containers #include "containers/binary_heap.h" +#include "containers/packed_ptr.h" #include "containers/trivial_array.h" #include "containers/trivial_buffer.h" #include "containers/trivial_vector.h" \ No newline at end of file diff --git a/include/ktl/utility/bits.h b/include/ktl/utility/bits.h new file mode 100644 index 0000000..0fd6d26 --- /dev/null +++ b/include/ktl/utility/bits.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace ktl::detail +{ + constexpr inline uintmax_t log2(uintmax_t n) + { + uintmax_t r = 0; + + if (n >> 32) { r += 32U; n >>= 32U; } + if (n >> 16) { r += 16U; n >>= 16U; } + if (n >> 8) { r += 8U; n >>= 8U; } + if (n >> 4) { r += 4U; n >>= 4U; } + if (n >> 2) { r += 2U; n >>= 2U; } + if (n >> 1) { r += 1U; n >>= 1U; } + + return r; + } +} \ No newline at end of file diff --git a/src/test/packed_ptr_test.cpp b/src/test/packed_ptr_test.cpp new file mode 100644 index 0000000..430a354 --- /dev/null +++ b/src/test/packed_ptr_test.cpp @@ -0,0 +1,47 @@ +#include "shared/assert_utility.h" +#include "shared/test.h" +#include "shared/types.h" + +#define KTL_DEBUG_ASSERT +#include "ktl/containers/packed_ptr.h" + +#include + +// Naming scheme: packed_ptr +// Contains tests that use the ktl::packed_ptr + +namespace ktl::test::packed_ptr +{ + KTL_ADD_TEST(test_packed_ptr_stack) + { + int t; + int* in_ptr = &t; + uint16_t in_value = 2; + + ktl::packed_ptr pack(in_ptr, in_value); + + KTL_TEST_ASSERT(pack); + KTL_TEST_ASSERT(in_ptr == pack.get_ptr()); + KTL_TEST_ASSERT(in_value == pack.get_int()); + } + + KTL_ADD_TEST(test_packed_ptr_malloc) + { + std::allocator alloc; + + double* in_ptr = alloc.allocate(1); + uint64_t in_value = 2; + + // Using more than 2 bits may require full x64 support + ktl::packed_ptr pack; + + pack.set_ptr(in_ptr); + pack.set_int(in_value); + + KTL_TEST_ASSERT(pack); + KTL_TEST_ASSERT(in_ptr == pack.get_ptr()); + KTL_TEST_ASSERT(in_value == pack.get_int()); + + alloc.deallocate(in_ptr, 1); + } +} \ No newline at end of file