diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 7236f25f..23dcd372 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -71,7 +71,7 @@ static int minfragment = MINFRAGMENT; // may be adjusted at runtime static memzone_t *mainzone; static memzone_t *smallzone; -static int mainzone_static[MAINZONE_STATIC_SIZE/sizeof(int)]; //static mainzone memory +int mainzone_static[MAINZONE_STATIC_SIZE/sizeof(int)]; //static mainzone memory // static const char *tagName[ TAG_COUNT ] = { "FREE", diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index 3f56c21f..f3691b1f 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -6,6 +6,7 @@ extern "C" { #endif +extern int mainzone_static[]; typedef struct zone_stats_s { int zoneSegments; diff --git a/code/tests/memory.cxx b/code/tests/memory.cxx new file mode 100644 index 00000000..adb89222 --- /dev/null +++ b/code/tests/memory.cxx @@ -0,0 +1,87 @@ +#include "memory.hxx" +#include "../qcommon/qcommon.h" +#include +#include +#include +#include + +namespace Memory { + +namespace Constants { + +static constexpr std::size_t kMemorySize = MAINZONE_STATIC_SIZE; +static constexpr int *kMemory = &mainzone_static[0]; + +}; // namespace Constants + +static std::shared_ptr currentFork = nullptr; +static std::vector> forks; + +}; // namespace Memory + +/** + * @brief Dumps current fork to memory + */ +static void DumpCurrentFork() { + if (!Memory::currentFork) + return; + + const int *currentForkBegin = Memory::currentFork.get(); + const int *currentForkEnd = Memory::currentFork.get() + + (Memory::Constants::kMemorySize / sizeof(int)); + + std::copy(currentForkBegin, currentForkEnd, Memory::Constants::kMemory); +} + +/** + * @brief Loads memory content to current fork + */ +static void LoadToCurrentFork() { + if (!Memory::currentFork) + return; + + const int *memoryBegin = Memory::Constants::kMemory; + const int *memoryEnd = Memory::Constants::kMemory + + (Memory::Constants::kMemorySize / sizeof(int)); + + std::copy(memoryBegin, memoryEnd, Memory::currentFork.get()); +} + +/** + * @brief Allocates and initializes new fork. + * + * @return int* - fork + */ +static std::shared_ptr ForkFromMainzone() { + std::shared_ptr fork( + new int[Memory::Constants::kMemorySize / sizeof(int)]); + + std::copy(Memory::Constants::kMemory, + Memory::Constants::kMemory + (MAINZONE_STATIC_SIZE / sizeof(int)), + fork.get()); + + return fork; +} + +std::size_t ForkMemory() { + // Syncronize previous fork with memory + LoadToCurrentFork(); + + Memory::currentFork = ForkFromMainzone(); + Memory::forks.push_back(Memory::currentFork); + + return Memory::forks.size() - 1; +} + +void SwitchFork(std::size_t id) { + if (Memory::currentFork == Memory::forks.at(id)) + return; + + // Syncronize previous fork with memory + LoadToCurrentFork(); + + Memory::currentFork = Memory::forks[id]; + + // Dump newly loaded fork into memory + DumpCurrentFork(); +} diff --git a/code/tests/memory.hxx b/code/tests/memory.hxx new file mode 100644 index 00000000..01d3fcca --- /dev/null +++ b/code/tests/memory.hxx @@ -0,0 +1,20 @@ +#pragma once +#include + +/** + * @brief Creates an independent copy of the current zone memory state and + * returns a unique fork ID. The new fork starts as an exact replica but + * evolves separately. + * + * @note Used in tests. Test group may share memory, but tests may be shuffled + * and we don't want them to corrupt each other's memory. + * + * @return std::size_t + */ +std::size_t ForkMemory(); + +/** + * @brief Activates the specified fork, making its memory state the current + * working context for subsequent operations. + */ +void SwitchFork(std::size_t id); diff --git a/code/tests/test_cg_superhud_configparser.cxx b/code/tests/test_cg_superhud_configparser.cxx index e163cbb1..1dd8056f 100644 --- a/code/tests/test_cg_superhud_configparser.cxx +++ b/code/tests/test_cg_superhud_configparser.cxx @@ -2,18 +2,19 @@ #include #include "../cgame/cg_superhud_private.h" #include "../qcommon/qcommon.h" +#include "memory.hxx" -namespace +static void shud_test_init() { - bool initialized = false; - void shud_test_init() - { - if (initialized) return; - Com_InitZoneMemory(); - CG_SHUDParserInit(); - initialized = true; - } + static bool initialized = false; + static std::size_t forkId = ForkMemory(); + + SwitchFork(forkId); + if (initialized) return; + Com_InitZoneMemory(); + CG_SHUDParserInit(); + initialized = true; } diff --git a/code/tests/test_osp_drawstring.cxx b/code/tests/test_osp_drawstring.cxx index 84730b88..5738e7fb 100644 --- a/code/tests/test_osp_drawstring.cxx +++ b/code/tests/test_osp_drawstring.cxx @@ -3,12 +3,19 @@ #include "../cgame/cg_local.h" #include "../qcommon/qcommon.h" +#include "memory.hxx" +static void InitZoneMemory() { + static std::size_t forkId = ForkMemory(); + + SwitchFork(forkId); + Com_InitZoneMemory(); +} TEST_CASE("Prepare string", "[cgame][cg_drawtools.c]") { char out[1024]; - Com_InitZoneMemory(); + InitZoneMemory(); CG_OSPDrawStringPrepare("text", out, 1024); CHECK(strncmp("text", out, 4) == 0); @@ -51,7 +58,7 @@ TEST_CASE("Prepare string", "[cgame][cg_drawtools.c]") TEST_CASE("Text compiler", "[API][cg_drawtools.c]") { - Com_InitZoneMemory(); + InitZoneMemory(); CHECK(CG_CompileText(NULL, 0) == NULL); CHECK(CG_CompileText("", 0) == NULL); @@ -332,7 +339,7 @@ TEST_CASE("Text compiler", "[API][cg_drawtools.c]") TEST_CASE("Text compiler: bug ^^0 ", "[API][cg_drawtools.c]") { - Com_InitZoneMemory(); + InitZoneMemory(); CHECK(CG_CompileText(NULL, 0) == NULL); CHECK(CG_CompileText("", 0) == NULL); diff --git a/code/tests/test_qcommon_zone_allocator.cxx b/code/tests/test_qcommon_zone_allocator.cxx index 7318ed45..96349bc1 100644 --- a/code/tests/test_qcommon_zone_allocator.cxx +++ b/code/tests/test_qcommon_zone_allocator.cxx @@ -1,11 +1,18 @@ #include #include "../qcommon/qcommon.h" +#include "memory.hxx" +static void InitZoneMemory() { + static std::size_t forkId = ForkMemory(); + + SwitchFork(forkId); + Com_InitZoneMemory(); +} TEST_CASE("Test memory allocator", "[qcommon][memory][zone_allocator]") { - Com_InitZoneMemory(); + InitZoneMemory(); void *ptr = Z_Malloc(100); @@ -16,7 +23,7 @@ TEST_CASE("Test memory allocator", "[qcommon][memory][zone_allocator]") TEST_CASE("Test memory allocator, overflow", "[qcommon][memory][zone_allocator]") { - Com_InitZoneMemory(); + InitZoneMemory(); // too big void *ptr1 = Z_Malloc(MAINZONE_STATIC_SIZE);