diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d7e8669..8644c01 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,6 +1,9 @@ name: Docs on: + push: + branches: [ master ] + paths: '**/*.md' workflow_dispatch: jobs: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6178505..cc73e68 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,6 +8,7 @@ on: paths-ignore: - '**/*.md' - '**/*.gitignore' + - '**/Doxyfile' jobs: Build: diff --git a/README.md b/README.md index d9c9d43..b4ce1c9 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,9 @@ This library also contains various containers that are STL compliant. | `size_t size() const` | Returns the current amount of elements in the vector. | # Allocator Examples +The following examples all have `using namespace ktl` or equivalent at the top for brevity. +These examples can all be seen as unit tests in [`src/test/exotic_allocator_test.cpp`](https://github.com/KredeGC/KTL/tree/master/src/test/exotic_allocator_test.cpp). + Create an allocator which will attempt to use a linear allocator for allocation, but fall back on malloc when full. ```cpp // Create the allocator from some 8kb buffer and straight malloc diff --git a/include/ktl/allocators/overflow.h b/include/ktl/allocators/overflow.h index e85a4b2..f4ec6ba 100644 --- a/include/ktl/allocators/overflow.h +++ b/include/ktl/allocators/overflow.h @@ -22,11 +22,15 @@ namespace ktl typedef typename detail::get_size_type_t size_type; private: - static constexpr int OVERFLOW_PATTERN = 0b01010101010101010101010101010101; - static constexpr int64_t OVERFLOW_TEST = 0b0101010101010101010101010101010101010101010101010101010101010101; - static constexpr size_t OVERFLOW_SIZE = 8; + static constexpr unsigned int OVERFLOW_PATTERN = 0b10100101101001011010010110100101; + static constexpr unsigned char OVERFLOW_TEST = 0b10100101; + static constexpr size_t OVERFLOW_SIZE = 64; public: + /** + * @brief Construct the allocator with a reference to a stream object + * @param stream The stream to use when leaks or corruption happens + */ explicit overflow(Stream& stream) noexcept : m_Stream(stream), m_Alloc(), @@ -85,6 +89,13 @@ namespace ktl } #pragma region Allocation + /** + * @brief Attempts to allocate a chunk of memory defined by @p n + * @note Allocates 64 bytes more on either side of the returned address. + * This memory will be used for overflow checking. + * @param n The amount of bytes to allocate memory for + * @return A location in memory that is at least @p n bytes big or nullptr if it could not be allocated + */ void* allocate(size_type n) { m_Allocs += n; @@ -101,6 +112,11 @@ namespace ktl return ptr + OVERFLOW_SIZE; } + /** + * @brief Attempts to deallocate the memory at location @p p + * @param p The location in memory to deallocate + * @param n The size that was initially allocated + */ void deallocate(void* p, size_type n) { KTL_ASSERT(p != nullptr); @@ -109,14 +125,34 @@ namespace ktl if (p) { - char* ptr = reinterpret_cast(p); + unsigned char* ptr = reinterpret_cast(p); + + size_t before = 0; + size_t after = 0; // Check against corruption - if (std::memcmp(ptr + n, &OVERFLOW_TEST, OVERFLOW_SIZE) != 0) - m_Stream << "--------MEMORY CORRUPTION DETECTED--------\nThe area around " << reinterpret_cast(ptr + n) << " has been illegally modified\n"; + for (unsigned char* i = ptr - 1; i >= ptr - OVERFLOW_SIZE; i--) + { + if (*i != OVERFLOW_TEST) + before = ptr - i; + } + + for (unsigned char* i = ptr + n; i < ptr + n + OVERFLOW_SIZE; i++) + { + if (*i != OVERFLOW_TEST) + after = i - ptr - n + 1; + } + + if (before || after) + { + m_Stream << "--------MEMORY CORRUPTION DETECTED--------\nThe area around " << p << " (" << n << " bytes) has been illegally modified\n"; - if (std::memcmp(ptr - OVERFLOW_SIZE, &OVERFLOW_TEST, OVERFLOW_SIZE) != 0) - m_Stream << "--------MEMORY CORRUPTION DETECTED--------\nThe area around " << reinterpret_cast(ptr - OVERFLOW_SIZE) << " has been illegally modified\n"; + if (before) + m_Stream << " Before (" << before << " bytes)\n"; + + if (after) + m_Stream << " After (" << after << " bytes)\n"; + } size_type size = n + OVERFLOW_SIZE * 2; m_Alloc.deallocate(ptr - OVERFLOW_SIZE, size); @@ -125,6 +161,13 @@ namespace ktl #pragma endregion #pragma region Construction + /** + * @brief Constructs an object of T with the given @p ...args at the given location + * @note Keeps track of the number of constructions + * @tparam ...Args The types of the arguments + * @param p The location of the object in memory + * @param ...args A range of arguments to use to construct the object + */ template void construct(T* p, Args&&... args) { @@ -136,6 +179,11 @@ namespace ktl ::new(p) T(std::forward(args)...); } + /** + * @brief Destructs an object of T at the given location + * @note Keeps track of the number of destructions + * @param p The location of the object in memory + */ template void destroy(T* p) { @@ -149,6 +197,11 @@ namespace ktl #pragma endregion #pragma region Utility + /** + * @brief Returns the maximum size that an allocation can be + * @note Only defined if the underlying allocator defines it + * @return The maximum size an allocation may be + */ template typename std::enable_if, size_type>::type max_size() const noexcept @@ -156,6 +209,12 @@ namespace ktl return m_Alloc.max_size(); } + /** + * @brief Returns whether or not the allocator owns the given location in memory + * @note Only defined if the underlying allocator defines it + * @param p The location of the object in memory + * @return Whether the allocator owns @p p + */ template typename std::enable_if, bool>::type owns(void* p) const @@ -164,11 +223,19 @@ namespace ktl } #pragma endregion + /** + * @brief Returns a reference to the underlying allocator + * @return The allocator + */ Alloc& get_allocator() { return m_Alloc; } + /** + * @brief Returns a const reference to the underlying allocator + * @return The allocator + */ const Alloc& get_allocator() const { return m_Alloc;