-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
352 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/// @file | ||
/// @brief Class @ref cubos::core::memory::UnorderedBimap. | ||
/// @ingroup core-memory | ||
|
||
#pragma once | ||
|
||
#include <cstddef> | ||
|
||
namespace cubos::core::reflection | ||
{ | ||
class Type; | ||
class ConstructibleTrait; | ||
} // namespace cubos::core::reflection | ||
|
||
namespace cubos::core::memory | ||
{ | ||
/// @brief Stores a dynamically sized array of blobs of a given reflected type. | ||
/// @ingroup core-memory | ||
class AnyVector final | ||
{ | ||
public: | ||
~AnyVector(); | ||
|
||
/// @brief Constructs with the given element type. | ||
/// @note @p elementType must have @ref reflection::ConstructibleTrait. | ||
/// @param elementType Element type. | ||
AnyVector(const reflection::Type& elementType); | ||
|
||
/// @brief Move constructs. | ||
/// @param other Vector. | ||
AnyVector(AnyVector&& other) noexcept; | ||
|
||
/// @brief Get the type of the elements stored in the vector. | ||
/// @return Element type. | ||
const reflection::Type& elementType() const; | ||
|
||
/// @brief Reserves space for at least @p capacity elements. | ||
/// @note Aborts if @p capacity is less than @ref size(). | ||
/// @param capacity Minimum capacity. | ||
void reserve(std::size_t capacity); | ||
|
||
/// @brief Pushes a new default-constructed element to the back of the vector. | ||
/// @note Aborts if @ref elementType() is not default-constructible. | ||
void pushDefault(); | ||
|
||
/// @brief Pushes a new copy-constructed element to the back of the vector. | ||
/// @note Aborts if @ref elementType() is not copy-constructible. | ||
/// @param element Element to copy. | ||
void pushCopy(const void* value); | ||
|
||
/// @brief Pushes a new move-constructed element to the back of the vector. | ||
/// @note Aborts if @ref elementType() is not move-constructible. | ||
/// @param element Element to move. | ||
void pushMove(void* value); | ||
|
||
/// @brief Removes the last element from the vector. | ||
/// @note Aborts if the vector is empty. | ||
void pop(); | ||
|
||
/// @brief Removes all elements from the vector. | ||
void clear(); | ||
|
||
/// @brief Get the element at the given index. | ||
/// @note Aborts if @p index is out of bounds. | ||
/// @param index Index of the element to get. | ||
/// @return Pointer to the element. | ||
void* at(std::size_t index); | ||
|
||
/// @copydoc at(std::size_t) | ||
const void* at(std::size_t index) const; | ||
|
||
/// @brief Get the number of elements in the vector. | ||
/// @return Element count. | ||
std::size_t size() const; | ||
|
||
/// @brief Get the number of elements the vector can hold without reallocating. | ||
/// @return Element capacity. | ||
std::size_t capacity() const; | ||
|
||
/// @brief Checks if the vector is empty. | ||
/// @return Whether the vector is empty. | ||
bool empty() const; | ||
|
||
private: | ||
const reflection::Type& mElementType; | ||
const reflection::ConstructibleTrait* mConstructibleTrait{nullptr}; | ||
void* mData{nullptr}; | ||
std::size_t mSize{0}; | ||
std::size_t mCapacity{0}; | ||
std::size_t mStride{0}; | ||
}; | ||
} // namespace cubos::core::memory |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
#include <cstdlib> | ||
|
||
#include <cubos/core/log.hpp> | ||
#include <cubos/core/memory/any_vector.hpp> | ||
#include <cubos/core/reflection/traits/constructible.hpp> | ||
#include <cubos/core/reflection/type.hpp> | ||
|
||
using cubos::core::memory::AnyVector; | ||
using cubos::core::reflection::ConstructibleTrait; | ||
using cubos::core::reflection::Type; | ||
|
||
AnyVector::~AnyVector() | ||
{ | ||
std::free(mData); | ||
} | ||
|
||
AnyVector::AnyVector(const Type& elementType) | ||
: mElementType(elementType) | ||
{ | ||
CUBOS_ASSERT(mElementType.has<ConstructibleTrait>(), "Type must be constructible"); | ||
mConstructibleTrait = &mElementType.get<ConstructibleTrait>(); | ||
|
||
CUBOS_ASSERT(mConstructibleTrait->size() % mConstructibleTrait->alignment() == 0, | ||
"Size must be a multiple of alignment"); | ||
mStride = mConstructibleTrait->size(); | ||
} | ||
|
||
AnyVector::AnyVector(AnyVector&& other) noexcept | ||
: mElementType(other.mElementType) | ||
, mConstructibleTrait(other.mConstructibleTrait) | ||
, mData(other.mData) | ||
, mSize(other.mSize) | ||
, mCapacity(other.mCapacity) | ||
, mStride(other.mStride) | ||
{ | ||
other.mSize = 0; | ||
other.mCapacity = 0; | ||
other.mData = nullptr; | ||
} | ||
|
||
const Type& AnyVector::elementType() const | ||
{ | ||
return mElementType; | ||
} | ||
|
||
void AnyVector::reserve(std::size_t capacity) | ||
{ | ||
CUBOS_ASSERT(capacity >= mSize, "Capacity must be greater than or equal to size"); | ||
|
||
if (mCapacity == capacity) | ||
{ | ||
return; | ||
} | ||
|
||
mCapacity = capacity; | ||
mData = std::realloc(mData, mStride * mCapacity); | ||
CUBOS_ASSERT(mData != nullptr, "Vector memory reallocation failed"); | ||
} | ||
|
||
void AnyVector::pushDefault() | ||
{ | ||
CUBOS_ASSERT(mConstructibleTrait->hasDefaultConstruct(), "Type must be default-constructible"); | ||
|
||
if (mSize == mCapacity) | ||
{ | ||
this->reserve(mCapacity == 0 ? 1 : mCapacity * 2); | ||
} | ||
|
||
mConstructibleTrait->defaultConstruct(static_cast<char*>(mData) + mStride * mSize); | ||
++mSize; | ||
} | ||
|
||
void AnyVector::pushCopy(const void* value) | ||
{ | ||
CUBOS_ASSERT(mConstructibleTrait->hasCopyConstruct(), "Type must be copy-constructible"); | ||
|
||
if (mSize == mCapacity) | ||
{ | ||
this->reserve(mCapacity == 0 ? 1 : mCapacity * 2); | ||
} | ||
|
||
mConstructibleTrait->copyConstruct(static_cast<char*>(mData) + mStride * mSize, value); | ||
++mSize; | ||
} | ||
|
||
void AnyVector::pushMove(void* value) | ||
{ | ||
CUBOS_ASSERT(mConstructibleTrait->hasMoveConstruct(), "Type must be move-constructible"); | ||
|
||
if (mSize == mCapacity) | ||
{ | ||
this->reserve(mCapacity == 0 ? 1 : mCapacity * 2); | ||
} | ||
|
||
mConstructibleTrait->moveConstruct(static_cast<char*>(mData) + mStride * mSize, value); | ||
++mSize; | ||
} | ||
|
||
void AnyVector::pop() | ||
{ | ||
CUBOS_ASSERT(mSize > 0, "Vector must not be empty"); | ||
|
||
--mSize; | ||
mConstructibleTrait->destruct(static_cast<char*>(mData) + mStride * mSize); | ||
} | ||
|
||
void AnyVector::clear() | ||
{ | ||
for (std::size_t i = 0; i < mSize; ++i) | ||
{ | ||
mConstructibleTrait->destruct(static_cast<char*>(mData) + mStride * i); | ||
} | ||
|
||
mSize = 0; | ||
} | ||
|
||
void* AnyVector::at(std::size_t index) | ||
{ | ||
CUBOS_ASSERT(index < mSize, "Index must be less than size"); | ||
return static_cast<char*>(mData) + mStride * index; | ||
} | ||
|
||
const void* AnyVector::at(std::size_t index) const | ||
{ | ||
CUBOS_ASSERT(index < mSize, "Index must be less than size"); | ||
return static_cast<char*>(mData) + mStride * index; | ||
} | ||
|
||
std::size_t AnyVector::size() const | ||
{ | ||
return mSize; | ||
} | ||
|
||
std::size_t AnyVector::capacity() const | ||
{ | ||
return mCapacity; | ||
} | ||
|
||
bool AnyVector::empty() const | ||
{ | ||
return mSize == 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
#include <doctest/doctest.h> | ||
|
||
#include <cubos/core/memory/any_vector.hpp> | ||
#include <cubos/core/reflection/external/primitives.hpp> | ||
#include <cubos/core/reflection/type.hpp> | ||
|
||
#include "../utils.hpp" | ||
|
||
TEST_CASE("memory::AnyVector") | ||
{ | ||
using cubos::core::memory::AnyVector; | ||
using cubos::core::reflection::reflect; | ||
|
||
SUBCASE("with integers") | ||
{ | ||
AnyVector vec{reflect<int>()}; | ||
CHECK(vec.elementType().is<int>()); | ||
|
||
SUBCASE("pushDefault") | ||
{ | ||
vec.pushDefault(); | ||
CHECK(vec.size() == 1); | ||
CHECK(vec.at(0) != nullptr); | ||
CHECK(*static_cast<const int*>(vec.at(0)) == 0); | ||
} | ||
|
||
SUBCASE("pushCopy/pushMove") | ||
{ | ||
int value = 42; | ||
|
||
SUBCASE("pushCopy") | ||
{ | ||
vec.pushCopy(&value); | ||
} | ||
|
||
SUBCASE("pushMove") | ||
{ | ||
vec.pushMove(&value); | ||
} | ||
|
||
CHECK(vec.size() == 1); | ||
CHECK(vec.at(0) != nullptr); | ||
CHECK(*static_cast<const int*>(vec.at(0)) == 42); | ||
} | ||
|
||
SUBCASE("move constructor") | ||
{ | ||
constexpr std::size_t Size = 100; | ||
|
||
for (std::size_t i = 0; i < Size; ++i) | ||
{ | ||
vec.pushDefault(); | ||
*static_cast<int*>(vec.at(i)) = static_cast<int>(i); | ||
} | ||
|
||
AnyVector vec2{std::move(vec)}; | ||
CHECK_FALSE(vec2.empty()); | ||
CHECK(vec2.size() == Size); | ||
|
||
for (std::size_t i = 0; i < Size; ++i) | ||
{ | ||
CHECK(*static_cast<const int*>(vec2.at(i)) == static_cast<int>(i)); | ||
} | ||
} | ||
|
||
CHECK(vec.capacity() >= vec.size()); | ||
} | ||
|
||
SUBCASE("detect destructor") | ||
{ | ||
AnyVector vec{reflect<DetectDestructor>()}; | ||
CHECK(vec.elementType().is<DetectDestructor>()); | ||
|
||
constexpr std::size_t Size = 100; | ||
bool destroyed[Size]; | ||
|
||
for (std::size_t i = 0; i < Size; ++i) | ||
{ | ||
CHECK(vec.size() == i); | ||
vec.pushDefault(); | ||
CHECK(vec.size() == i + 1); | ||
CHECK(vec.capacity() >= i + 1); | ||
|
||
destroyed[i] = false; | ||
static_cast<DetectDestructor*>(vec.at(i))->set(&destroyed[i]); | ||
} | ||
|
||
SUBCASE("pop until empty") | ||
{ | ||
for (std::size_t i = 0; i < Size; ++i) | ||
{ | ||
CHECK_FALSE(destroyed[Size - i - 1]); | ||
vec.pop(); | ||
CHECK(destroyed[Size - i - 1]); | ||
} | ||
} | ||
|
||
SUBCASE("clear") | ||
{ | ||
vec.clear(); | ||
} | ||
|
||
for (const auto& i : destroyed) | ||
{ | ||
CHECK(i); | ||
} | ||
|
||
CHECK(vec.empty()); | ||
CHECK(vec.size() == 0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters