Simplified VectorArena to always take an arena and to not support change of capacity

This commit is contained in:
2026-07-22 22:12:05 -04:00
parent c0d40ef3e4
commit 9c8f9bff41
12 changed files with 213 additions and 197 deletions
+92
View File
@@ -0,0 +1,92 @@
# Workload Analysis: Removing C++ Stdlib & Migrating to Pure C
This analysis outlines the required workload, technical impact, and migration strategy for:
1. Removing all standard C++ library dependencies (`std::*`) from the Juliet codebase.
2. Moving completely from C++ to pure C.
---
## 1. Scope Overview
The repository consists of multiple sub-projects (**Juliet Core Engine**, **JulietApp**, **Game**, **Romeo**, and **External dependencies**). Eliminating stdlib dependencies versus migrating completely to pure C represent two distinct milestones:
| Metric / Domain | Task 1: Eliminate C++ stdlib | Task 2: Pure C Migration |
| :--- | :--- | :--- |
| **Language Target** | C++20 (without standard libraries) | Pure C (C11 / C99 standard) |
| **Containers & Math** | Custom templates (`Vector<T>`, `HashTable<K,V>`, custom algorithms) | C structs + `void*` / macro templates + explicit functions |
| **Threading & Sync** | Win32 / OS APIs directly (`CreateThread`, `SRWLOCK`) | Native OS APIs (`CreateThread`, `SRWLOCK`, atomic primitives) |
| **External Third-Party** | Native Direct3D 12, ImGui | `cimgui` (C bindings), DirectX C COM interfaces (`ID3D12Device*`) |
| **OOP & Templates** | Kept intact | Struct-based vtables, no function overloading, no references (`&`) |
---
## 2. Phase 1: Removing C++ stdlib Dependencies
### A. Containers & Utility Replacement
- **Current Usages**: `<vector>`, `<queue>`, `<list>`, `<forward_list>`, `<algorithm>`, `<bit>`, `<concepts>`, `<type_traits>`, `<source_location>`.
- **Workload**:
1. **Containers**: Replace container implementations (such as `Juliet/include/Core/Container/Vector.h`, which currently includes `<vector>`) with custom dynamically resizing arrays / buffer allocators backed by `MemoryArena`.
2. **Type Traits & Concepts**: Replace `<type_traits>` and `<concepts>` usage in files like `Game/Entity/Entity.h` with compiler intrinsics (e.g. `__is_enum`, `__is_base_of`) or internal lightweight trait structures.
3. **Algorithms & Math Utilities**: Implement custom `Min`, `Max`, `Clamp`, `Sort`, and `Swap` functions in `CoreUtils.h` or custom inline header helpers.
### B. Threading & Synchronization
- **Current Usages**: `<thread>`, `<mutex>`, `std::thread`, `std::mutex`, `std::lock_guard`.
- **Workload**:
1. Replace `<thread>` in `Thread.h` and `Romeo/src/main.cpp` with platform abstraction wrappers using Win32 `CreateThread` / `CloseHandle`.
2. Replace `<mutex>` in `Mutex.h` with `SRWLOCK` or `CRITICAL_SECTION`.
### C. Time & File I/O
- **Current Usages**: `<chrono>`, `<cstdio>`, `<fstream>`, `<ios>`, `<iosfwd>`, `<cstdlib>`.
- **Workload**:
1. Replace `<chrono>` in `LogManager.cpp` and `SystemEvent.cpp` with `QueryPerformanceCounter` / `QueryPerformanceFrequency` on Windows.
2. Standardize filesystem I/O on Win32 API (`CreateFileW`, `ReadFile`, `WriteFile`) or minimal C runtime headers (`<stdio.h>`, `<stdlib.h>`, `<string.h>`).
### D. Memory Allocators & RTTI
- **Current Usages**: `<new>`, `<typeinfo>` (`typeid` in `MemoryArena.h`), `<memory>`.
- **Workload**:
1. Replace placement `new` with custom inline placement functions or custom explicit initialization methods.
2. Replace `typeid` / `type_info` usages with custom type hashing or enum type IDs.
---
## 3. Phase 2: Complete Migration to C
Transitioning fully to C requires refactoring away all C++ language constructs.
### A. DirectX 12 & Agility SDK Integration
- **D3D12 Header Interface**: Switch from C++ COM smart pointers / methods (`device->CreateCommandAllocator(...)`) to standard C COM interfaces (`ID3D12Device_CreateCommandAllocator(device, ...)`).
- **D3DX12 Helper Library**: `d3dx12_state_object.h` and helper files rely heavily on stdlib (`std::vector`, `std::memory`, C++ structures). These must be rewritten in plain C or replaced with native DirectX 12 C struct initializers.
### B. UI Framework (ImGui)
- Dear ImGui is natively C++.
- **Workload**: Switch from standard `imgui.h` / `imgui_stdlib.h` to [cimgui](https://github.com/cimgui/cimgui) (C-compatible wrappers for ImGui) or maintain a minimal C++ bridge file specifically for ImGui.
### C. Codebase Refactoring (Language Paradigm)
1. **Classes to Structs**: Convert classes in `Juliet` core, `Game`, and `Romeo` to C structs.
2. **Virtual Functions**: Replace polymorphism with explicit `void*` context pointers and function pointer tables (vtables).
3. **Namespaces & Function Overloading**: Prefix function names (e.g., `Juliet_Log_Info(...)`) as C does not support namespaces or function overloading.
4. **Member Functions to Free Functions**: Convert `obj.Method(args)` to `Method(&obj, args)`.
5. **No Reference Arguments (`&`)**: Replace all pass-by-reference parameters with explicit pointers (`*`).
6. **Casting Rules**: Remove `static_cast` and `reinterpret_cast` in favor of standard C explicit casts `(Type)val`.
### D. Build System (`Romeo.bff` / `fbuild.bff`)
- Update compiler settings in FastBuild (`.bff` files) and Visual Studio project settings from `/std:c++20` to C compiler flags (`/std:c11` or `/TC`).
---
## 4. Estimated Workload Matrix
| Task Module | Complexity | Estimated Relative Effort | Risks / Key Considerations |
| :--- | :--- | :--- | :--- |
| **1. Stdlib-Free Containers & Utils** | Moderate | ~15% | Custom array/hash table implementation. |
| **2. OS Threading & Time Abstraction** | Low-Moderate | ~10% | Clean Windows API replacements (`QueryPerformanceCounter`, `SRWLOCK`). |
| **3. C Struct & Syntax Refactoring** | High | ~40% | Large volume of mechanical refactoring (references, namespaces, overloading, methods). |
| **4. Graphics / D3D12 C Refactoring** | High | ~20% | `d3dx12` heavily relies on C++ templates/stdlib; rewriting helper structs in C. |
| **5. ImGui / Romeo GUI Integration** | Moderate | ~15% | Needs `cimgui` integration or a C-wrapper boundary. |
---
## 5. Recommendation & Phasing
1. **Step 1 (Stdlib Removal)**: Keep C++20 language features (classes, templates, member methods), but remove all stdlib inclusions (`<vector>`, `<thread>`, `<chrono>`, `<algorithm>`). Replace them with custom `Juliet` engine primitives.
2. **Step 2 (Evaluate C Migration)**: Re-evaluate if moving to pure C provides enough benefit vs. keeping a clean, stdlib-free C++ subset ("C with Classes" / stdlib-free C++).
+54 -82
View File
@@ -2,50 +2,29 @@
#include <Core/Common/NonNullPtr.h> #include <Core/Common/NonNullPtr.h>
#include <Core/Memory/MemoryArena.h> #include <Core/Memory/MemoryArena.h>
#include <vector>
namespace Juliet namespace Juliet
{ {
template <typename Type, size_t ReserveSize = 16, bool AllowRealloc = false> template <typename Type, size_t ReserveSize = 16>
struct VectorArena struct VectorArena
{ {
void Create(JULIET_DEBUG_PARAM_FIRST(const char* name = nullptr))
{
Assert(!Arena);
static_assert(AllowRealloc == false);
DataFirst = DataLast = nullptr;
Count = 0;
JULIET_DEBUG_ONLY(Name = name ? name : Name;)
ArenaParams params{ .AllowRealloc = AllowRealloc JULIET_DEBUG_PARAM(.CanReserveMore = false) };
Arena = ArenaAllocate(params JULIET_DEBUG_PARAM(Name));
InternalArena = true;
Reserve(ReserveSize);
}
void Create(NonNullPtr<Arena> arena JULIET_DEBUG_PARAM(const char* name = nullptr)) void Create(NonNullPtr<Arena> arena JULIET_DEBUG_PARAM(const char* name = nullptr))
{ {
Assert(!Arena); Assert(!Arena);
JULIET_DEBUG_ONLY(Name = name ? name : Name;) JULIET_DEBUG_ONLY(Name = name ? name : Name;)
DataFirst = DataLast = nullptr; DataFirst = DataLast = Data = nullptr;
Count = 0; Count = 0;
Arena = arena.Get(); Capacity = 0;
InternalArena = false; Arena = arena;
Reserve(ReserveSize); Reserve(ReserveSize);
} }
void Destroy() void Destroy()
{ {
if (InternalArena) DataFirst = DataLast = Data = nullptr;
{
ArenaRelease(Arena);
}
DataFirst = DataLast = nullptr;
Count = 0; Count = 0;
Capacity = 0; Capacity = 0;
Arena = nullptr; Arena = nullptr;
@@ -53,38 +32,33 @@ namespace Juliet
void Reserve(size_t newCapacity) void Reserve(size_t newCapacity)
{ {
if (newCapacity > Capacity) Assert(Arena);
{
if (Data == nullptr) if (Data == nullptr)
{ {
Data = ArenaPushArray<Type>(Arena, newCapacity JULIET_DEBUG_PARAM(Name)); Data = ArenaPushArray<Type>(Arena, newCapacity JULIET_DEBUG_PARAM(Name));
}
else if constexpr (AllowRealloc)
{
if (!InternalArena)
{
DataFirst = Data =
static_cast<Type*>(ArenaReallocate(Arena, Data, Capacity * sizeof(Type), newCapacity * sizeof(Type),
AlignOf(Type), true JULIET_DEBUG_PARAM("VectorRealloc")));
DataLast = Data + Count - 1;
}
}
Capacity = newCapacity; Capacity = newCapacity;
} }
else
{
Assert(newCapacity <= Capacity && "VectorArena capacity exceeded! Reallocation is disabled.");
}
} }
void Resize(size_t newCount) void Resize(size_t newCount)
{ {
Assert(Arena);
if (newCount == Count) if (newCount == Count)
{ {
return; return;
} }
if (newCount > Capacity) if (Data == nullptr)
{ {
Reserve(newCount); size_t initialCapacity = newCount > ReserveSize ? newCount : ReserveSize;
Reserve(initialCapacity);
} }
Assert(newCount <= Capacity && "VectorArena capacity exceeded!");
Count = newCount; Count = newCount;
if (Count > 0) if (Count > 0)
@@ -101,20 +75,21 @@ namespace Juliet
void PushBack(const Type* buffer, size_t amount) void PushBack(const Type* buffer, size_t amount)
{ {
Assert(Arena); Assert(Arena);
Assert(buffer || amount == 0);
if (Count + amount > Capacity) if (amount == 0)
{ {
if (Capacity == 0 && Count + amount < ReserveSize) return;
}
if (Data == nullptr)
{ {
Reserve(ReserveSize); size_t initialCapacity = amount > ReserveSize ? amount : ReserveSize;
} Reserve(initialCapacity);
else
{
size_t newCapacity = Max(Capacity * 2, AlignPow2(Capacity + amount, AlignOf(Type)));
Reserve(newCapacity);
}
} }
Assert(Count + amount <= Capacity && "VectorArena capacity exceeded!");
Type* dst = Data + Count; Type* dst = Data + Count;
MemCopy(dst, buffer, amount * sizeof(Type)); MemCopy(dst, buffer, amount * sizeof(Type));
@@ -122,7 +97,7 @@ namespace Juliet
{ {
DataFirst = dst; DataFirst = dst;
} }
DataLast = dst + amount; DataLast = dst + amount - 1;
Count += amount; Count += amount;
} }
@@ -130,11 +105,13 @@ namespace Juliet
{ {
Assert(Arena); Assert(Arena);
if (Count + 1 > Capacity) if (Data == nullptr)
{ {
Reserve(Capacity == 0 ? ReserveSize : Capacity * 2); Reserve(ReserveSize);
} }
Assert(Count + 1 <= Capacity && "VectorArena capacity exceeded!");
Type* entry = Data + Count; Type* entry = Data + Count;
*entry = value; *entry = value;
@@ -150,11 +127,13 @@ namespace Juliet
{ {
Assert(Arena); Assert(Arena);
if (Count + 1 > Capacity) if (Data == nullptr)
{ {
Reserve(Capacity == 0 ? ReserveSize : Capacity * 2); Reserve(ReserveSize);
} }
Assert(Count + 1 <= Capacity && "VectorArena capacity exceeded!");
Type* entry = Data + Count; Type* entry = Data + Count;
*entry = std::move(value); *entry = std::move(value);
@@ -193,44 +172,37 @@ namespace Juliet
{ {
Assert(Arena); Assert(Arena);
if (InternalArena)
{
ArenaClear(Arena);
Data = nullptr;
Capacity = 0;
Reserve(ReserveSize);
}
DataFirst = DataLast = nullptr; DataFirst = DataLast = nullptr;
Count = 0; Count = 0;
} }
bool IsEmpty() const { return Count == 0; } [[nodiscard]] bool IsEmpty() const { return Count == 0; }
// C++ Accessors for loop supports and Index based access // C++ Accessors for loop supports and Index based access
Type& operator[](size_t index) { return DataFirst[index]; } [[nodiscard]] Type& operator[](size_t index) { return DataFirst[index]; }
const Type& operator[](size_t index) const { return DataFirst[index]; } [[nodiscard]] const Type& operator[](size_t index) const { return DataFirst[index]; }
Type* begin() { return DataFirst; } [[nodiscard]] Type* begin() { return DataFirst; }
Type* end() { return DataFirst + Count; } [[nodiscard]] Type* end() { return DataFirst + Count; }
const Type* begin() const { return DataFirst; } [[nodiscard]] const Type* begin() const { return DataFirst; }
const Type* end() const { return DataFirst + Count; } [[nodiscard]] const Type* end() const { return DataFirst + Count; }
Type* First() { return DataFirst; } [[nodiscard]] Type* First() { return DataFirst; }
Type* Front() { return DataFirst; } [[nodiscard]] Type* Front() { return DataFirst; }
Type* Last() { return DataLast; } [[nodiscard]] Type* Last() { return DataLast; }
Type* Back() { return DataLast; } [[nodiscard]] Type* Back() { return DataLast; }
[[nodiscard]] Type* DataPtr() { return Data; }
[[nodiscard]] const Type* DataPtr() const { return Data; }
size_t Size() const { return Count; } [[nodiscard]] size_t Size() const { return Count; }
Arena* Arena; Arena* Arena = nullptr;
Type* DataFirst; Type* DataFirst = nullptr;
Type* DataLast; Type* DataLast = nullptr;
Type* Data; Type* Data = nullptr;
size_t Count; size_t Count = 0;
size_t Capacity; size_t Capacity = 0;
bool InternalArena : 1;
JULIET_DEBUG_ONLY(const char* Name = "VectorArena";) JULIET_DEBUG_ONLY(const char* Name = "VectorArena";)
}; };
static_assert(std::is_standard_layout_v<VectorArena<int>>, static_assert(std::is_standard_layout_v<VectorArena<int>>,
@@ -5,25 +5,20 @@
namespace Juliet namespace Juliet
{ {
// TODO Use VectorArena
template <typename T>
class Vector : public std::vector<T>
{
};
class NetworkPacket class NetworkPacket
{ {
public: public:
NetworkPacket(); NetworkPacket();
NetworkPacket(Arena& arena);
virtual ~NetworkPacket(); virtual ~NetworkPacket();
NetworkPacket(NetworkPacket&); NetworkPacket(NetworkPacket&);
NetworkPacket& operator=(const NetworkPacket&); NetworkPacket& operator=(const NetworkPacket&);
NetworkPacket(NetworkPacket&&) noexcept; NetworkPacket(NetworkPacket&&) noexcept;
NetworkPacket& operator=(NetworkPacket&&) noexcept; NetworkPacket& operator=(NetworkPacket&&) noexcept;
ByteBuffer GetRawData(); void Create(Arena& arena);
// Unpack [[nodiscard]] ByteBuffer GetRawData();
// Pack // Pack
NetworkPacket& operator<<(uint32 value); NetworkPacket& operator<<(uint32 value);
@@ -35,7 +30,7 @@ namespace Juliet
friend class TcpSocket; friend class TcpSocket;
private: private:
Vector<Byte> Data; VectorArena<Byte, 4096> Data;
size_t PartialSendIndex = 0; size_t PartialSendIndex = 0;
}; };
} // namespace Juliet } // namespace Juliet
+4 -5
View File
@@ -30,11 +30,10 @@ namespace Juliet
struct MeshRenderer struct MeshRenderer
{ {
// Note we prevent realloc for now. VectorArena<Mesh, kDefaultMeshNumber> Meshes;
VectorArena<Mesh, kDefaultMeshNumber, false> Meshes; VectorArena<Vertex, kDefaultVertexCount> Vertices;
VectorArena<Vertex, kDefaultVertexCount, false> Vertices; VectorArena<Index, kDefaultIndexCount> Indices;
VectorArena<Index, kDefaultIndexCount, false> Indices; VectorArena<PointLight, kDefaultLightCount> PointLights;
VectorArena<PointLight, kDefaultLightCount, false> PointLights;
GraphicsBuffer* VertexBuffer; GraphicsBuffer* VertexBuffer;
GraphicsBuffer* IndexBuffer; GraphicsBuffer* IndexBuffer;
+12 -5
View File
@@ -7,17 +7,26 @@
namespace Juliet namespace Juliet
{ {
NetworkPacket::NetworkPacket() = default; NetworkPacket::NetworkPacket() = default;
NetworkPacket::NetworkPacket(Arena& arena)
{
Data.Create(&arena);
}
NetworkPacket::~NetworkPacket() = default; NetworkPacket::~NetworkPacket() = default;
NetworkPacket::NetworkPacket(NetworkPacket&) = default; NetworkPacket::NetworkPacket(NetworkPacket&) = default;
NetworkPacket& NetworkPacket::operator=(const NetworkPacket&) = default; NetworkPacket& NetworkPacket::operator=(const NetworkPacket&) = default;
NetworkPacket::NetworkPacket(NetworkPacket&&) noexcept = default; NetworkPacket::NetworkPacket(NetworkPacket&&) noexcept = default;
NetworkPacket& NetworkPacket::operator=(NetworkPacket&&) noexcept = default; NetworkPacket& NetworkPacket::operator=(NetworkPacket&&) noexcept = default;
void NetworkPacket::Create(Arena& arena)
{
Data.Create(&arena);
}
ByteBuffer NetworkPacket::GetRawData() ByteBuffer NetworkPacket::GetRawData()
{ {
ByteBuffer buffer{}; ByteBuffer buffer{};
buffer.Data = Data.data(); buffer.Data = Data.DataPtr();
buffer.Size = Data.size(); buffer.Size = Data.Size();
return buffer; return buffer;
} }
@@ -52,9 +61,7 @@ namespace Juliet
{ {
if (buffer.Data && (buffer.Size > 0)) if (buffer.Data && (buffer.Size > 0))
{ {
const auto* begin = reinterpret_cast<const std::byte*>(buffer.Data); Data.PushBack(TO_BUFFER(buffer.Data), buffer.Size);
const auto* end = begin + buffer.Size;
Data.insert(Data.end(), begin, end);
} }
} }
+3
View File
@@ -27,6 +27,7 @@ namespace Juliet
// Because of that we will send the size of the packet first before sending the data. // Because of that we will send the size of the packet first before sending the data.
// TODO Use scratch allocator. // TODO Use scratch allocator.
/*
Vector<Byte> scratchVector; Vector<Byte> scratchVector;
scratchVector.resize(buffer.Size + sizeof(uint32)); scratchVector.resize(buffer.Size + sizeof(uint32));
@@ -52,6 +53,8 @@ namespace Juliet
} }
return messageStatus; return messageStatus;
*/
return {};
} }
Socket::RequestStatus TcpSocket::Send(ByteBuffer buffer) Socket::RequestStatus TcpSocket::Send(ByteBuffer buffer)
+5 -3
View File
@@ -40,13 +40,14 @@ namespace Juliet::Debug
}; };
// Static Vector for lookup // Static Vector for lookup
VectorArena<PagedArenaStateEntry> s_PagedArenaStates; VectorArena<PagedArenaStateEntry, Kilobytes(1024)> s_PagedArenaStates;
PagedArenaDebugState& GetPagedArenaState(const Arena* arena) PagedArenaDebugState& GetPagedArenaState(const Arena* arena)
{ {
if (s_PagedArenaStates.Arena == nullptr) if (s_PagedArenaStates.Arena == nullptr)
{ {
s_PagedArenaStates.Create(JULIET_DEBUG_ONLY("DebugState States")); Arena* pagedArenaStates = ArenaAllocate({} JULIET_DEBUG_ONLY(, "DebugState States"));
s_PagedArenaStates.Create(pagedArenaStates);
} }
for (auto& entry : s_PagedArenaStates) for (auto& entry : s_PagedArenaStates)
@@ -104,7 +105,8 @@ namespace Juliet::Debug
static VectorArena<const Arena*> blocks; static VectorArena<const Arena*> blocks;
if (blocks.Arena == nullptr) if (blocks.Arena == nullptr)
{ {
blocks.Create(JULIET_DEBUG_ONLY("DebugState Blocks")); Arena* pagedArenaStates = ArenaAllocate({} JULIET_DEBUG_ONLY(, "DebugState Blocks"));
blocks.Create(pagedArenaStates);
} }
blocks.Clear(); blocks.Clear();
+4 -4
View File
@@ -22,10 +22,10 @@ namespace Juliet
GraphicsBuffer* TransformsBuffer = nullptr; GraphicsBuffer* TransformsBuffer = nullptr;
GraphicsTransferBuffer* LoadCopyBuffer = nullptr; GraphicsTransferBuffer* LoadCopyBuffer = nullptr;
VectorArena<Mesh, kDefaultMeshNumber, false> Meshes; VectorArena<Mesh, kDefaultMeshNumber> Meshes;
VectorArena<Vertex, kDefaultVertexCount, false> Vertices; VectorArena<Vertex, kDefaultVertexCount> Vertices;
VectorArena<Index, kDefaultIndexCount, false> Indices; VectorArena<Index, kDefaultIndexCount> Indices;
VectorArena<PointLight, kDefaultLightCount, false> PointLights; VectorArena<PointLight, kDefaultLightCount> PointLights;
PointLight* MappedLights = nullptr; PointLight* MappedLights = nullptr;
Matrix* MappedTransforms = nullptr; Matrix* MappedTransforms = nullptr;
@@ -9,8 +9,8 @@ namespace Juliet::UnitTest
{ {
namespace namespace
{ {
template <typename T, size_t ReserveSize, bool AllowRealloc> template <typename T, size_t ReserveSize>
inline void VerifyVectorState(const VectorArena<T, ReserveSize, AllowRealloc>& vec, size_t expectedCount) inline void VerifyVectorState(const VectorArena<T, ReserveSize>& vec, size_t expectedCount)
{ {
Assert(vec.Size() == expectedCount); Assert(vec.Size() == expectedCount);
@@ -29,10 +29,13 @@ namespace Juliet::UnitTest
void VectorUnitTest() void VectorUnitTest()
{ {
ArenaParams params{};
Arena* testArena = ArenaAllocate(params JULIET_DEBUG_ONLY(, "VectorUnitTestArena"));
// Test 1: Integer VectorArena (Basic Operations) // Test 1: Integer VectorArena (Basic Operations)
{ {
VectorArena<int> vec = {}; VectorArena<int> vec = {};
vec.Create(); vec.Create(testArena);
VerifyVectorState(vec, 0); VerifyVectorState(vec, 0);
vec.PushBack(10); vec.PushBack(10);
@@ -65,7 +68,7 @@ namespace Juliet::UnitTest
// Test 2: RemoveAtFast // Test 2: RemoveAtFast
{ {
VectorArena<int> vec = {}; VectorArena<int> vec = {};
vec.Create(); vec.Create(testArena);
vec.PushBack(10); vec.PushBack(10);
vec.PushBack(20); vec.PushBack(20);
@@ -95,7 +98,7 @@ namespace Juliet::UnitTest
// Test 3: Clear and Reuse (Pointer State Check) // Test 3: Clear and Reuse (Pointer State Check)
{ {
VectorArena<int> vec = {}; VectorArena<int> vec = {};
vec.Create(); vec.Create(testArena);
vec.PushBack(100); vec.PushBack(100);
vec.PushBack(200); vec.PushBack(200);
@@ -127,7 +130,7 @@ namespace Juliet::UnitTest
}; };
VectorArena<TestItem> vec = {}; VectorArena<TestItem> vec = {};
vec.Create(); vec.Create(testArena);
VerifyVectorState(vec, 0); VerifyVectorState(vec, 0);
@@ -145,39 +148,7 @@ namespace Juliet::UnitTest
vec.Destroy(); vec.Destroy();
} }
// Test 5: Reallocation Support (AllowRealloc = true) // Test 5: Move Semantics
{
// Initial capacity 4, allow realloc
ArenaParams params{ .AllowRealloc = true };
Arena* externalArena = ArenaAllocate(params JULIET_DEBUG_ONLY(, "VectorTest"));
VectorArena<int, 4, true> vec = {};
vec.Create(externalArena);
Assert(vec.Capacity == 4);
vec.PushBack(1);
vec.PushBack(2);
vec.PushBack(3);
vec.PushBack(4);
Assert(vec.Capacity == 4);
// Trigger realloc
vec.PushBack(5);
Assert(vec.Capacity >= 5); // Should have grown (likely doubled to 8)
VerifyVectorState(vec, 5);
Assert(vec[0] == 1);
Assert(vec[1] == 2);
Assert(vec[2] == 3);
Assert(vec[3] == 4);
Assert(vec[4] == 5);
vec.Destroy();
ArenaRelease(externalArena);
}
// Test 6: Move Semantics
{ {
struct MoveOnly struct MoveOnly
{ {
@@ -207,10 +178,8 @@ namespace Juliet::UnitTest
} }
}; };
VectorArena<MoveOnly, 4, true> vec = {}; VectorArena<MoveOnly, 4> vec = {};
ArenaParams params{ .AllowRealloc = true }; NonNullPtr<Arena> validArena(testArena);
Arena* externalArena = ArenaAllocate(params JULIET_DEBUG_ONLY(, "VectorTest"));
NonNullPtr<Arena> validArena(externalArena);
vec.Create(validArena); vec.Create(validArena);
MoveOnly item1(10); MoveOnly item1(10);
@@ -222,16 +191,13 @@ namespace Juliet::UnitTest
Assert(!vec[0].MovedFrom); Assert(!vec[0].MovedFrom);
vec.Destroy(); vec.Destroy();
ArenaRelease(externalArena);
} }
// Test 7: Resize Behavior // Test 6: Resize Behavior
{ {
ArenaParams params{ .AllowRealloc = true }; NonNullPtr<Arena> validArena(testArena);
Arena* externalArena = ArenaAllocate(params JULIET_DEBUG_ONLY(, "VectorTest"));
NonNullPtr<Arena> validArena(externalArena);
VectorArena<int, 4, true> vec = {}; VectorArena<int, 4> vec = {};
vec.Create(validArena); vec.Create(validArena);
vec.PushBack(1); vec.PushBack(1);
@@ -243,42 +209,21 @@ namespace Juliet::UnitTest
Assert(vec.Size() == 1); Assert(vec.Size() == 1);
Assert(vec[0] == 1); Assert(vec[0] == 1);
// Resize larger // Resize larger up to max capacity
vec.Resize(4); vec.Resize(4);
Assert(vec.Size() == 4); Assert(vec.Size() == 4);
Assert(vec[0] == 1); Assert(vec[0] == 1);
vec.Destroy(); vec.Destroy();
ArenaRelease(externalArena);
} }
// Test 8: External Arena Usage // Test 7: External Arena Usage & Volume Test within capacity
{ {
ArenaParams params{ .AllowRealloc = true }; NonNullPtr<Arena> validArena(testArena);
Arena* externalArena = ArenaAllocate(params JULIET_DEBUG_ONLY(, "VectorTest"));
NonNullPtr<Arena> validArena(externalArena);
VectorArena<int> vec = {}; VectorArena<int, 100> vec = {};
vec.Create(validArena); // Pass external arena vec.Create(validArena); // Pass external arena
vec.PushBack(42);
Assert(vec.Size() == 1);
Assert(vec[0] == 42);
vec.Destroy();
ArenaRelease(externalArena);
}
// Test 9: Volume Test (100 items with Realloc)
{
ArenaParams params{ .AllowRealloc = true };
Arena* externalArena = ArenaAllocate(params JULIET_DEBUG_ONLY(, "VectorTest"));
NonNullPtr<Arena> validArena(externalArena);
VectorArena<int, 4, true> vec = {};
vec.Create(validArena);
// Push 100 items -> Should realloc multiple times
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
vec.PushBack(i); vec.PushBack(i);
@@ -291,8 +236,9 @@ namespace Juliet::UnitTest
} }
vec.Destroy(); vec.Destroy();
ArenaRelease(externalArena);
} }
ArenaRelease(testArena);
} }
} // namespace Juliet::UnitTest } // namespace Juliet::UnitTest
#endif #endif
+1 -1
View File
@@ -25,7 +25,7 @@ namespace Romeo
struct Database struct Database
{ {
Juliet::VectorArena<FileEntry, 512, true> Entries = {}; Juliet::VectorArena<FileEntry, 512> Entries = {};
Juliet::Arena* DbArena = nullptr; Juliet::Arena* DbArena = nullptr;
Juliet::Mutex Mutex; Juliet::Mutex Mutex;
}; };
+2 -2
View File
@@ -31,8 +31,8 @@ namespace Romeo
double NumVal = 0.0; double NumVal = 0.0;
Juliet::String StrVal = {}; Juliet::String StrVal = {};
Juliet::VectorArena<JsonValue, 32, true> ArrayVal = {}; Juliet::VectorArena<JsonValue, 32> ArrayVal = {};
Juliet::VectorArena<JsonKeyValue, 32, true> ObjectVal = {}; Juliet::VectorArena<JsonKeyValue, 32> ObjectVal = {};
}; };
// Parses a JSON string. Memory is allocated on the provided arena. // Parses a JSON string. Memory is allocated on the provided arena.
+3 -3
View File
@@ -410,7 +410,7 @@ namespace Romeo
static void ParseSymbolsFromFile( static void ParseSymbolsFromFile(
Juliet::Arena* arena, Juliet::Arena* arena,
const Juliet::String& filePath, const Juliet::String& filePath,
Juliet::VectorArena<ParsedSymbol, 128, true>& outSymbols Juliet::VectorArena<ParsedSymbol, 128>& outSymbols
) )
{ {
Assert(arena != nullptr); Assert(arena != nullptr);
@@ -726,7 +726,7 @@ namespace Romeo
static void FormatAndWriteSymbols(StringBuilder& sb, Juliet::Arena* arena, const Juliet::String& filePath) static void FormatAndWriteSymbols(StringBuilder& sb, Juliet::Arena* arena, const Juliet::String& filePath)
{ {
printf("Romeo: FormatAndWriteSymbols start for %.*s\n", static_cast<int>(filePath.Size), CStr(filePath)); printf("Romeo: FormatAndWriteSymbols start for %.*s\n", static_cast<int>(filePath.Size), CStr(filePath));
Juliet::VectorArena<ParsedSymbol, 128, true> symbols = {}; Juliet::VectorArena<ParsedSymbol, 128> symbols = {};
symbols.Create(arena JULIET_DEBUG_ONLY(, "ParsedSymbols")); symbols.Create(arena JULIET_DEBUG_ONLY(, "ParsedSymbols"));
printf("Romeo: Calling ParseSymbolsFromFile...\n"); printf("Romeo: Calling ParseSymbolsFromFile...\n");
@@ -741,7 +741,7 @@ namespace Romeo
// Collect all distinct namespaces // Collect all distinct namespaces
printf("Romeo: Collecting namespaces...\n"); printf("Romeo: Collecting namespaces...\n");
Juliet::VectorArena<Juliet::String, 16, true> namespaces = {}; Juliet::VectorArena<Juliet::String, 16> namespaces = {};
namespaces.Create(arena JULIET_DEBUG_ONLY(, "NamespacesList")); namespaces.Create(arena JULIET_DEBUG_ONLY(, "NamespacesList"));
for (const ParsedSymbol& sym : symbols) for (const ParsedSymbol& sym : symbols)