From 59dbc15b3f489e72913c9a8ff13565f83a65be5f Mon Sep 17 00:00:00 2001 From: Drischdaan Date: Thu, 25 Sep 2025 22:45:11 +0200 Subject: [PATCH 1/3] feat(core): adds core concepts --- .../Runtime/Core/Public/Core/CoreConcepts.hpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Engine/Source/Runtime/Core/Public/Core/CoreConcepts.hpp diff --git a/Engine/Source/Runtime/Core/Public/Core/CoreConcepts.hpp b/Engine/Source/Runtime/Core/Public/Core/CoreConcepts.hpp new file mode 100644 index 0000000..0915479 --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/Core/CoreConcepts.hpp @@ -0,0 +1,18 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include +#include + +template +concept CThreeWayComparable = requires(const T& a, const T& b) +{ + { a <=> b } -> std::convertible_to; +}; + +template +concept CEqualityComparable = requires(const T& a, const T& b) +{ + { a == b } -> std::convertible_to; +}; From ed4989a06a7d676118ec03597bdbf0fc4e9de32c Mon Sep 17 00:00:00 2001 From: Drischdaan Date: Thu, 25 Sep 2025 22:45:41 +0200 Subject: [PATCH 2/3] feat(core): adds static array class --- .../Public/Core/Containers/StaticArray.hpp | 368 ++++++++++++++++++ 1 file changed, 368 insertions(+) create mode 100644 Engine/Source/Runtime/Core/Public/Core/Containers/StaticArray.hpp diff --git a/Engine/Source/Runtime/Core/Public/Core/Containers/StaticArray.hpp b/Engine/Source/Runtime/Core/Public/Core/Containers/StaticArray.hpp new file mode 100644 index 0000000..d24ad9b --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/Core/Containers/StaticArray.hpp @@ -0,0 +1,368 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include +#include +#include +#include +#include + +#include "Core/CoreConcepts.hpp" + +template requires std::is_object_v && !std::is_abstract_v && std::is_trivially_constructible_v && (TSize > 0) +class TStaticArray +{ +public: + constexpr TStaticArray() + : Data() + { + } + + constexpr explicit TStaticArray(const TElement& InInitialValue) + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = InInitialValue; + } + } + + constexpr explicit TStaticArray(std::initializer_list InInitializerList) + { + const size64 Count = InInitializerList.size() < TSize ? InInitializerList.size() : TSize; + for (size64 Index = 0; Index < Count; ++Index) + { + Data[Index] = InInitializerList[Index]; + } + } + + constexpr TStaticArray(const TStaticArray& Other) + { + if constexpr (std::is_trivially_copyable_v) + { + if (std::is_constant_evaluated()) + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = Other.Data[Index]; + } + } + else + { + std::memcpy(Data, Other.Data, sizeof(Data)); + } + } + else + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = Other.Data[Index]; + } + } + } + + constexpr TStaticArray(TStaticArray&& Other) noexcept + { + if constexpr (std::is_trivially_copyable_v) + { + if (std::is_constant_evaluated()) + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = std::move(Other.Data[Index]); + } + } + else + { + std::memcpy(Data, Other.Data, sizeof(Data)); + } + } + else + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = std::move(Other.Data[Index]); + } + } + } + + constexpr ~TStaticArray() requires std::is_trivially_destructible_v = default; + + ~TStaticArray() requires (!std::is_trivially_destructible_v) + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index].~TElement(); + } + } + +public: + constexpr TStaticArray& operator=(const TStaticArray& Other) + { + if (this != &Other) + { + if constexpr (std::is_trivially_copyable_v) + { + if (std::is_constant_evaluated()) + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = Other.Data[Index]; + } + } + else + { + std::memcpy(Data, Other.Data, sizeof(Data)); + } + } + else + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = Other.Data[Index]; + } + } + } + return *this; + } + + constexpr TStaticArray& operator=(TStaticArray&& Other) noexcept + { + if (this != &Other) + { + if constexpr (std::is_trivially_copyable_v) + { + if (std::is_constant_evaluated()) + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = std::move(Other.Data[Index]); + } + } + else + { + std::memcpy(Data, Other.Data, sizeof(Data)); + } + } + else + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = std::move(Other.Data[Index]); + } + } + } + return *this; + } + + constexpr TElement& operator[](size64 Index) + { + assert(Index >= 0 && Index < TSize && "Array index is out of bounds"); + return Data[Index]; + } + + constexpr const TElement& operator[](size64 Index) const + { + assert(Index >= 0 && Index < TSize && "Array index is out of bounds"); + return Data[Index]; + } + + constexpr auto operator<=>(const TStaticArray& Other) const noexcept requires CThreeWayComparable + { + for (size64 Index = 0; Index < TSize; ++Index) + { + if (auto Result = Data[Index] <=> Other.Data[Index]; Result != 0) + { + return Result; + } + } + return std::strong_ordering::equal; + } + + constexpr bool8 operator==(const TStaticArray& Other) const noexcept requires CEqualityComparable + { + for (size64 Index = 0; Index < TSize; ++Index) + { + if (!(Data[Index] == Other.Data[Index])) + { + return false; + } + } + return true; + } + +public: + [[nodiscard]] constexpr TElement& At(size64 Index) + { + return (*this)[Index]; + } + + [[nodiscard]] constexpr const TElement& At(size64 Index) const + { + return (*this)[Index]; + } + + [[nodiscard]] constexpr TElement& GetFirst() + { + return Data[0]; + } + + [[nodiscard]] constexpr const TElement& GetFirst() const + { + return Data[0]; + } + + [[nodiscard]] constexpr TElement& GetLast() + { + return Data[TSize - 1]; + } + + [[nodiscard]] constexpr const TElement& GetLast() const + { + return Data[TSize - 1]; + } + + [[nodiscard]] constexpr TElement* GetData() noexcept + { + return Data; + } + + [[nodiscard]] constexpr const TElement* GetData() const noexcept + { + return Data; + } + +public: + constexpr void Fill(const TElement& Value) + { + for (size64 Index = 0; Index < TSize; ++Index) + { + Data[Index] = Value; + } + } + + [[nodiscard]] constexpr size64 Find(const TElement& Item) const requires CEqualityComparable + { + for (size64 Index = 0; Index < TSize; ++Index) + { + if (Data[Index] == Item) + { + return Index; + } + } + return std::numeric_limits::max(); + } + + [[nodiscard]] constexpr bool8 Contains(const TElement& Item) const requires CEqualityComparable + { + return Find(Item) != std::numeric_limits::max(); + } + +public: + template TFunc> + constexpr void ForEach(TFunc&& Function) + { + for (size64 Index = 0; Index < TSize; ++Index) + { + std::invoke(std::forward(Function), Data[Index]); + } + } + + template TFunc> + constexpr void ForEach(TFunc&& Function) const + { + for (size64 Index = 0; Index < TSize; ++Index) + { + std::invoke(std::forward(Function), Data[Index]); + } + } + + template TPredicate> + [[nodiscard]] constexpr bool8 AllOf(TPredicate&& Predicate) const + { + for (size64 Index = 0; Index < TSize; ++Index) + { + if (!std::invoke(std::forward(Predicate), Data[Index])) + { + return false; + } + } + return true; + } + + template TPredicate> + [[nodiscard]] constexpr bool8 AnyOf(TPredicate&& Predicate) const + { + for (size64 Index = 0; Index < TSize; ++Index) + { + if (std::invoke(std::forward(Predicate), Data[Index])) + { + return true; + } + } + return false; + } + + template TPredicate> + [[nodiscard]] constexpr size64 CountIf(TPredicate&& Predicate) const + { + size64 Count = 0; + for (size64 Index = 0; Index < TSize; ++Index) + { + if (std::invoke(std::forward(Predicate), Data[Index])) + { + ++Count; + } + } + return Count; + } + +public: + // Iterator Support + [[nodiscard]] constexpr TElement* begin() noexcept + { + return Data; + } + + [[nodiscard]] constexpr const TElement* begin() const noexcept + { + return Data; + } + + [[nodiscard]] constexpr TElement* end() noexcept + { + return Data + TSize; + } + + [[nodiscard]] constexpr const TElement* end() const noexcept + { + return Data + TSize; + } + + [[nodiscard]] constexpr const TElement* cbegin() const noexcept + { + return Data; + } + + [[nodiscard]] constexpr const TElement* cend() const noexcept + { + return Data + TSize; + } + +public: + [[nodiscard]] static constexpr size64 Num() noexcept + { + return TSize; + } + + [[nodiscard]] static constexpr size64 GetSizeInBytes() noexcept + { + return TSize * sizeof(TElement); + } + +private: + TElement Data[TSize]; +}; + +template +TStaticArray(TArguments...) -> TStaticArray, sizeof...(TArguments)>; From 2b07a0b9e5a358491bc83d1776934a35a9bfac9b Mon Sep 17 00:00:00 2001 From: Drischdaan Date: Thu, 25 Sep 2025 22:45:52 +0200 Subject: [PATCH 3/3] feat(core): adds array class --- .../Core/Public/Core/Containers/Array.hpp | 420 ++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 Engine/Source/Runtime/Core/Public/Core/Containers/Array.hpp diff --git a/Engine/Source/Runtime/Core/Public/Core/Containers/Array.hpp b/Engine/Source/Runtime/Core/Public/Core/Containers/Array.hpp new file mode 100644 index 0000000..ce80065 --- /dev/null +++ b/Engine/Source/Runtime/Core/Public/Core/Containers/Array.hpp @@ -0,0 +1,420 @@ +// RavenStorm Copyright @ 2025-2025 + +#pragma once + +#include "Core/CoreConcepts.hpp" +#include "Core/Memory/Memory.hpp" + +#include +#include + +template requires std::is_object_v && !std::is_abstract_v +class TArray +{ +private: + static constexpr size64 DefaultCapacity = 4; + static constexpr size64 GrowthFactor = 2; + +public: + constexpr TArray() = default; + + explicit TArray(const size64 InCount) requires std::is_trivially_constructible_v + { + if (InCount > 0) + { + Reserve(InCount); + for (size64 Index = 0; Index < InCount; ++Index) + { + EmplaceBack(); + } + } + } + + TArray(const size64 InCount, const TElement& InValue) + { + if (InCount > 0) + { + Reserve(InCount); + for (size64 Index = 0; Index < InCount; ++Index) + { + EmplaceBack(InValue); + } + } + } + + TArray(std::initializer_list InInitializerList) + { + if (InInitializerList.size() > 0) + { + Reserve(InInitializerList.size()); + for (const auto& Element : InInitializerList) + { + EmplaceBack(Element); + } + } + } + + TArray(const TArray& Other) + { + if (Other.Size > 0) + { + Reserve(Other.Size); + for (size64 Index = 0; Index < Other.Size; ++Index) + { + EmplaceBack(Other.Data[Index]); + } + } + } + + TArray(TArray&& Other) noexcept + : Data(Other.Data), Size(Other.Size), Capacity(Other.Capacity) + { + Other.Data = nullptr; + Other.Size = 0; + Other.Capacity = 0; + } + + ~TArray() + { + DestroyAndDeallocate(); + } + +public: + TArray& operator=(const TArray& Other) + { + if (this != &Other) + { + Clear(); + if (Other.Size > 0) + { + Reserve(Other.Size); + for (size64 Index = 0; Index < Other.Size; ++Index) + { + EmplaceBack(Other.Data[Index]); + } + } + } + return *this; + } + + TArray& operator=(TArray&& Other) noexcept + { + if (this != &Other) + { + DestroyAndDeallocate(); + + Data = Other.Data; + Size = Other.Size; + Capacity = Other.Capacity; + + Other.Data = nullptr; + Other.Size = 0; + Other.Capacity = 0; + } + return *this; + } + + TArray& operator=(std::initializer_list InInitializerList) + { + Clear(); + if (InInitializerList.size() > 0) + { + Reserve(InInitializerList.size()); + for (const auto& Element : InInitializerList) + { + EmplaceBack(Element); + } + } + return *this; + } + + constexpr TElement& operator[](const size64 Index) + { + assert(Index < Size && "Array index is out of bounds"); + return Data[Index]; + } + + constexpr const TElement& operator[](const size64 Index) const + { + assert(Index < Size && "Array index is out of bounds"); + return Data[Index]; + } + + auto operator<=>(const TArray& Other) const noexcept requires CThreeWayComparable + { + const size64 MinSize = Size < Other.Size ? Size : Other.Size; + for (size64 Index = 0; Index < MinSize; ++Index) + { + if (bool8 Result = Data[Index] <=> Other.Data[Index]; Result != 0) + { + return Result; + } + } + return Size <=> Other.Size; + } + + bool8 operator==(const TArray& Other) const noexcept requires CEqualityComparable + { + if (Size != Other.Size) + { + return false; + } + for (size64 Index = 0; Index < Size; ++Index) + { + if (!(Data[Index] == Other.Data[Index])) + { + return false; + } + } + return true; + } + +public: +public: + [[nodiscard]] TElement& At(const size64 Index) + { + assert(Index < Size && "Array index is out of bounds"); + return Data[Index]; + } + + [[nodiscard]] const TElement& At(const size64 Index) const + { + assert(Index < Size && "Array index is out of bounds"); + return Data[Index]; + } + + [[nodiscard]] TElement& GetFirst() + { + assert(Size > 0 && "Cannot call GetFirst() on empty array"); + return Data[0]; + } + + [[nodiscard]] const TElement& GetFirst() const + { + assert(Size > 0 && "Cannot call GetFirst() on empty array"); + return Data[0]; + } + + [[nodiscard]] TElement& GetLast() + { + assert(Size > 0 && "Cannot call GetLast() on empty array"); + return Data[Size - 1]; + } + + [[nodiscard]] const TElement& GetLast() const + { + assert(Size > 0 && "Cannot call GetLast() on empty array"); + return Data[Size - 1]; + } + + [[nodiscard]] TElement* GetData() noexcept + { + return Data; + } + + [[nodiscard]] const TElement* GetData() const noexcept + { + return Data; + } + +public: + void PushBack(const TElement& InValue) + { + EmplaceBack(InValue); + } + + void PushBack(TElement&& InValue) + { + EmplaceBack(std::move(InValue)); + } + + template + TElement& EmplaceBack(TArguments&&... Arguments) requires std::is_constructible_v + { + if (Size >= Capacity) + { + const size64 NewCapacity = Capacity == 0 ? DefaultCapacity : Capacity * GrowthFactor; + Reserve(NewCapacity); + } + TElement* NewElement = std::construct_at(&Data[Size], std::forward(Arguments)...); + ++Size; + return *NewElement; + } + + void PopBack() + { + assert(Size > 0 && "Cannot pop from empty array"); + --Size; + std::destroy_at(&Data[Size]); + } + + void Reserve(const size64 NewCapacity) + { + if (NewCapacity > Capacity) + { + TElement* NewData = static_cast(FMemory::Allocate(sizeof(TElement) * NewCapacity)); + if (Data != nullptr) + { + if constexpr (std::is_trivially_copyable_v) + { + FMemory::Copy(Data, NewData, Size * sizeof(TElement)); + } + else + { + for (size64 Index = 0; Index < Size; ++Index) + { + std::construct_at(&NewData[Index], std::move(Data[Index])); + std::destroy_at(&Data[Index]); + } + } + + FMemory::Free(Data); + } + Data = NewData; + Capacity = NewCapacity; + } + } + + void Resize(const size64 NewSize) + { + if (NewSize > Size) + { + Reserve(NewSize); + for (size64 Index = Size; Index < NewSize; ++Index) + { + std::construct_at(&Data[Index]); + } + } + else if (NewSize < Size) + { + for (size64 Index = NewSize; Index < Size; ++Index) + { + std::destroy_at(&Data[Index]); + } + } + Size = NewSize; + } + + void ShrinkToFit() + { + if (Capacity > Size) + { + if (Size == 0) + { + FMemory::Free(Data); + Data = nullptr; + Capacity = 0; + } + else + { + TElement* NewData = static_cast(FMemory::Allocate(Size * sizeof(TElement))); + if constexpr (std::is_trivially_copyable_v) + { + FMemory::Copy(Data, NewData, Size * sizeof(TElement)); + } + else + { + for (size64 Index = 0; Index < Size; ++Index) + { + std::construct_at(&NewData[Index], std::move(Data[Index])); + std::destroy_at(&Data[Index]); + } + } + FMemory::Free(Data); + Data = NewData; + Capacity = Size; + } + } + } + + void Clear() + { + for (size64 Index = 0; Index < Size; ++Index) + { + std::destroy_at(&Data[Index]); + } + Size = 0; + } + +public: + [[nodiscard]] size64 Num() const noexcept + { + return Size; + } + + [[nodiscard]] size64 GetCapacity() const noexcept + { + return Capacity; + } + + [[nodiscard]] size64 GetSizeInBytes() const noexcept + { + return Size * sizeof(TElement); + } + + [[nodiscard]] size64 GetCapacityInBytes() const noexcept + { + return Capacity * sizeof(TElement); + } + + [[nodiscard]] bool8 IsEmpty() const noexcept + { + return Size == 0; + } + +public: + // TElement* support + [[nodiscard]] TElement* begin() noexcept + { + return Data; + } + + [[nodiscard]] const TElement* begin() const noexcept + { + return Data; + } + + [[nodiscard]] TElement* end() noexcept + { + return Data + Size; + } + + [[nodiscard]] const TElement* end() const noexcept + { + return Data + Size; + } + + [[nodiscard]] const TElement* cbegin() const noexcept + { + return Data; + } + + [[nodiscard]] const TElement* cend() const noexcept + { + return Data + Size; + } + +private: + void DestroyAndDeallocate() + { + if (Data != nullptr) + { + for (size64 Index = 0; Index < Size; ++Index) + { + std::destroy_at(&Data[Index]); + } + FMemory::Free(Data); + Data = nullptr; + } + Size = 0; + Capacity = 0; + } + +private: + TElement* Data = nullptr; + size64 Size = 0; + size64 Capacity = 0; +}; + +template +TArray(TArguments...) -> TArray>;