From 7dd0eb961a830230e8a3c57427a9107f5a6f6be5 Mon Sep 17 00:00:00 2001 From: Kearwood Gilbert Date: Sat, 15 Aug 2026 02:05:44 -0700 Subject: [PATCH] Add Arena Allocator - Adding files that were not staged --- include/arena.h | 59 ++++++++++++++ src/arena.cpp | 203 +++++++++++++++++++++++++++++++++++++++++++++++ src/mimir.cpp | 100 +++++++++++++++++++++++ src/mimir_impl.h | 50 ++++++++++++ 4 files changed, 412 insertions(+) create mode 100644 include/arena.h create mode 100644 src/arena.cpp create mode 100644 src/mimir.cpp create mode 100644 src/mimir_impl.h diff --git a/include/arena.h b/include/arena.h new file mode 100644 index 0000000..785adb2 --- /dev/null +++ b/include/arena.h @@ -0,0 +1,59 @@ +// +// arena.h +// Kraken Engine / Mimir +// +// Copyright 2026 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#pragma once + +#include +#include + +namespace mimir { +class Arena +{ +public: + Arena(size_t minSize = 1ULL << 24, size_t maxSize = 1ULL << 32); + ~Arena(); + + std::byte* alloc(size_t size); + std::byte* allocA16(size_t size); + std::byte* allocA64(size_t size); + void reset(); +private: + std::byte* m_data; + size_t m_minSize; + size_t m_maxSize; + size_t m_committedSize; + size_t m_usedSize; + + static const size_t kWatermarkLen = 8; + size_t m_watermark[kWatermarkLen]; +}; // class Arena + +} // namespace mimir diff --git a/src/arena.cpp b/src/arena.cpp new file mode 100644 index 0000000..44532a7 --- /dev/null +++ b/src/arena.cpp @@ -0,0 +1,203 @@ +// +// arena.cpp +// Kraken Engine +// +// Copyright 2026 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "../include/mimir.h" +#include "mimir_impl.h" + +#if defined(__unix__) || defined(__APPLE__) || defined(ANDROID) +#include +#include +#include +#include +#endif + +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#endif + +#include +#include +#include +#include + +namespace mimir { + +struct ArenaControlBlock +{ + size_t allocated; + size_t comitted; + size_t next; +}; + +static_assert(sizeof(ArenaControlBlock) < 4096); + +Arena::Arena(size_t minSize, size_t maxSize) + : m_minSize(KRAKEN_MEM_ROUND_UP_PAGE(minSize)) + , m_maxSize(KRAKEN_MEM_ROUND_UP_PAGE(maxSize)) + , m_data(nullptr) + , m_usedSize(0) + , m_committedSize(0) +{ + assert(maxSize >= minSize); + + for (size_t i = 0; i < kWatermarkLen; i++) { + m_watermark[i] = 0; + } +} + +Arena::~Arena() +{ + if (m_data) { +#if defined(_WIN32) || defined(_WIN64) + if (!VirtualFree((void*)m_data, 0, MEM_RELEASE)) { + ReportWindowsLastError("VirtualAlloc"); + } +#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID) + munmap(m_data, m_maxSize); +#else + static_assert(false, "Not Implemented"); +#endif + } +} + +std::byte* Arena::alloc(size_t size) +{ + size_t neededSize = m_usedSize + size; + + if (m_data == nullptr) { +#if defined(_WIN32) || defined(_WIN64) + m_data = (std::byte*)VirtualAlloc(NULL, m_maxSize, MEM_RESERVE, PAGE_NOACCESS); + if (m_data == nullptr) { + ReportWindowsLastError("VirtualAlloc"); + return nullptr; + } +#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID) + m_data = (std::byte*)mmap(NULL, m_maxSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (m_data == MAP_FAILED) { + return nullptr; + } +#else + static_assert(false, "Not Implemented"); +#endif + } + + if (neededSize < m_committedSize) + { + size_t newSize = std::bit_ceil(neededSize) << 1; + if (newSize < m_minSize) { + newSize = m_minSize; + } + newSize = KRAKEN_MEM_ROUND_UP_PAGE(newSize); + +#if defined(_WIN32) || defined(_WIN64) + if(VirtualAlloc(m_data + m_committedSize, newSize - m_committedSize, MEM_COMMIT, PAGE_READWRITE) == nullptr) { + ReportWindowsLastError("VirtualAlloc"); + return nullptr; + } +#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID) + if (mprotect(m_data + m_committedSize, newSize - m_committedSize, PROT_READ | PROT_WRITE) != 0) { + return nullptr; + } +#else + static_assert(false, "Not Implemented"); +#endif + m_committedSize = newSize; + } // if (neededSize < m_committedSize) + + std::byte* ret = m_data + m_usedSize; + m_usedSize += neededSize; + return ret; +} + +std::byte* Arena::allocA16(size_t size) +{ + uint32_t nextByte = (m_usedSize + 15) & ~15; + uint32_t roundedSize = (size + 15) & ~15; + if (alloc(size) == nullptr) { + return nullptr; + } + + return m_data + nextByte; +} + +std::byte* Arena::allocA64(size_t size) +{ + uint32_t nextByte = (m_usedSize + 63) & ~63; + uint32_t roundedSize = (size + 63) & ~63; + if (alloc(size) == nullptr) { + return nullptr; + } + + return m_data + nextByte; +} + +void Arena::reset() +{ + size_t highWatermark = m_usedSize; + for (size_t i = 0; i < kWatermarkLen - 1; i++) { + m_watermark[i] = m_watermark[i + 1]; + if (m_watermark[i] > highWatermark) { + highWatermark = m_watermark[i]; + } + } + m_watermark[kWatermarkLen - 1] = m_usedSize; + + size_t targetSize = std::bit_ceil(highWatermark) << 1; + size_t thresholdSize = targetSize << 1; // The threshold for shrinking is greater than the target to implement hysteresis + if (m_committedSize > thresholdSize) { + size_t newSize = KRAKEN_MEM_ROUND_UP_PAGE(targetSize); + + std::byte* rangeStart = m_data + newSize; + size_t rangeLen = m_committedSize - newSize; +#if defined(_WIN32) || defined(_WIN64) + if (!VirtualAlloc(rangeStart, rangeLen, MEM_RESET, PAGE_NOACCESS)) { + ReportWindowsLastError("VirtualAlloc"); + } else { + m_committedSize = newSize; + } +#elif defined(__unix__) || defined(__APPLE__) || defined(ANDROID) + if (madvise(rangeStart, rangeLen, MADV_DONTNEED) != 0) { + // TODO - Log Error or debug build assert + } + if (mprotect(rangeStart, rangeLen, PROT_NONE) != 0) { + // TODO - Log Error or debug build assert + } +#else + static_assert(false, "Not Implemented"); +#endif + m_committedSize = newSize; + } + + m_usedSize = 0; +} + +} // namespace mimir diff --git a/src/mimir.cpp b/src/mimir.cpp new file mode 100644 index 0000000..a955741 --- /dev/null +++ b/src/mimir.cpp @@ -0,0 +1,100 @@ +// +// block.cpp +// Kraken Engine +// +// Copyright 2026 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#include "../include/mimir.h" +#include "mimir_impl.h" + +#if defined(__unix__) || defined(__APPLE__) || defined(ANDROID) +#include +#include +#include +#include +#endif + +#include +#include +#include + +namespace mimir { + +int KRENGINE_SYS_ALLOCATION_GRANULARITY; +int KRENGINE_SYS_PAGE_SIZE; + +void init() +{ +#if defined(_WIN32) || defined(_WIN64) + + SYSTEM_INFO winSysInfo; + GetSystemInfo(&winSysInfo); + KRENGINE_SYS_ALLOCATION_GRANULARITY = winSysInfo.dwAllocationGranularity; + KRENGINE_SYS_PAGE_SIZE = winSysInfo.dwPageSize; + +#elif defined(__APPLE__) || __linux__ || defined(ANDROID) + + KRENGINE_SYS_PAGE_SIZE = getpagesize(); + KRENGINE_SYS_ALLOCATION_GRANULARITY = KRENGINE_SYS_PAGE_SIZE; + +#else +#error Unsupported +#endif +} + +#if defined(_WIN32) || defined(_WIN64) +void ReportWindowsLastError(LPCTSTR lpszFunction) +{ + LPVOID lpMsgBuf; + LPVOID lpDisplayBuf; + DWORD dw = GetLastError(); + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + 0, NULL); + + // Display the error message and exit the process + + lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, + (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); + fprintf(stderr, + TEXT("%s failed with error %d: %s\n"), + lpszFunction, dw, (LPCTSTR)lpMsgBuf); + + LocalFree(lpMsgBuf); + LocalFree(lpDisplayBuf); +} +#endif + +} // namespace mimir diff --git a/src/mimir_impl.h b/src/mimir_impl.h new file mode 100644 index 0000000..66e4dd9 --- /dev/null +++ b/src/mimir_impl.h @@ -0,0 +1,50 @@ +// +// mimir_impl.h +// Kraken Engine / Mimir +// +// Copyright 2026 Kearwood Gilbert. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The views and conclusions contained in the software and documentation are those of the +// authors and should not be interpreted as representing official policies, either expressed +// or implied, of Kearwood Gilbert. +// + +#pragma once + +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + +namespace mimir { + +extern int KRENGINE_SYS_ALLOCATION_GRANULARITY; +extern int KRENGINE_SYS_PAGE_SIZE; + +#define KRAKEN_MEM_ROUND_DOWN_PAGE(x) ((x) & ~(KRENGINE_SYS_ALLOCATION_GRANULARITY - 1)) +#define KRAKEN_MEM_ROUND_UP_PAGE(x) ((((x) - 1) & ~(KRENGINE_SYS_ALLOCATION_GRANULARITY - 1)) + KRENGINE_SYS_ALLOCATION_GRANULARITY) + +#if defined(_WIN32) || defined(_WIN64) +void ReportWindowsLastError(LPCTSTR lpszFunction); +#endif + +} // namespace mimir