aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2024-11-19 09:59:04 +0100
committerGitHub Enterprise <[email protected]>2024-11-19 09:59:04 +0100
commite335ca10a6c6a1e37b155e2155f5c5908c0272ae (patch)
tree78b32384df0e58b22052dcfa7990c16921c6b650 /src
parent5.5.12 (diff)
downloadzen-e335ca10a6c6a1e37b155e2155f5c5908c0272ae.tar.xz
zen-e335ca10a6c6a1e37b155e2155f5c5908c0272ae.zip
memory/string support cleanup and additions (#220)
* removed unused memory classes * added align.h alignment helpers used in upcoming changes * added char16_t StringLength * avoid memory alloc in SetCurrentThreadName * added command line parsing helpers to zencore/commandline.h * removed IoBuffer direct VirtualAlloc path
Diffstat (limited to 'src')
-rw-r--r--src/zencore/commandline.cpp72
-rw-r--r--src/zencore/include/zencore/commandline.h39
-rw-r--r--src/zencore/include/zencore/iobuffer.h1
-rw-r--r--src/zencore/include/zencore/memory.h41
-rw-r--r--src/zencore/include/zencore/memory/align.h67
-rw-r--r--src/zencore/include/zencore/string.h6
-rw-r--r--src/zencore/iobuffer.cpp22
-rw-r--r--src/zencore/memory.cpp115
-rw-r--r--src/zencore/thread.cpp6
9 files changed, 188 insertions, 181 deletions
diff --git a/src/zencore/commandline.cpp b/src/zencore/commandline.cpp
new file mode 100644
index 000000000..c801bf151
--- /dev/null
+++ b/src/zencore/commandline.cpp
@@ -0,0 +1,72 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zencore/commandline.h>
+#include <zencore/string.h>
+
+#if ZEN_PLATFORM_WINDOWS
+# include <zencore/windows.h>
+ZEN_THIRD_PARTY_INCLUDES_START
+# include <shellapi.h> // For command line parsing
+ZEN_THIRD_PARTY_INCLUDES_END
+#endif
+
+#include <functional>
+
+namespace zen {
+
+void
+IterateCommandlineArgs(std::function<void(const std::string_view& Arg)>& ProcessArg)
+{
+#if ZEN_PLATFORM_WINDOWS
+ int ArgC = 0;
+ const LPWSTR CmdLine = ::GetCommandLineW();
+ const LPWSTR* ArgV = ::CommandLineToArgvW(CmdLine, &ArgC);
+
+ if (ArgC > 1)
+ {
+ for (int i = 1; i < ArgC; ++i)
+ {
+ StringBuilder<4096> ArgString8;
+
+ WideToUtf8(ArgV[i], ArgString8);
+
+ ProcessArg(ArgString8);
+ }
+ }
+
+ ::LocalFree(HLOCAL(ArgV));
+#elif ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
+ if (FILE* CmdLineFile = fopen("/proc/self/cmdline", "r"))
+ {
+ const char* ArgV[255] = {};
+ int ArgC = 0;
+
+ char* Arg = nullptr;
+ size_t Size = 0;
+ while (getdelim(&Arg, &Size, 0, CmdLineFile) != -1)
+ {
+ ArgV[ArgC++] = Arg;
+ Arg = nullptr; // getdelim will allocate buffer for next Arg
+ }
+ fclose(CmdLineFile);
+
+ if (ArgC > 1)
+ {
+ for (int i = 1; i < ArgC; ++i)
+ {
+ ProcessArg(ArgV[i]);
+ }
+ }
+
+ // cleanup after getdelim
+ while (ArgC > 0)
+ {
+ free((void*)ArgV[--ArgC]);
+ }
+ }
+#else
+# error Unknown platform
+#endif
+}
+
+} // namespace zen
diff --git a/src/zencore/include/zencore/commandline.h b/src/zencore/include/zencore/commandline.h
new file mode 100644
index 000000000..a4ce6b27d
--- /dev/null
+++ b/src/zencore/include/zencore/commandline.h
@@ -0,0 +1,39 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <zenbase/zenbase.h>
+
+#include <functional>
+#include <string_view>
+
+namespace zen {
+
+void IterateCommandlineArgs(std::function<void(const std::string_view& Arg)>& ProcessArg);
+
+template<typename Func>
+void
+IterateCommaSeparatedValue(std::string_view OptionArgs, Func&& ProcessArg)
+{
+ while (OptionArgs.size())
+ {
+ const auto CommaPos = OptionArgs.find_first_of(',');
+ std::string_view OptionArg;
+
+ if (CommaPos == std::string_view::npos)
+ {
+ // No comma or final argument
+ OptionArg = OptionArgs;
+ OptionArgs = {};
+ }
+ else
+ {
+ OptionArg = OptionArgs.substr(0, CommaPos);
+ OptionArgs = OptionArgs.substr(CommaPos + 1);
+ }
+
+ ProcessArg(OptionArg);
+ }
+}
+
+} // namespace zen
diff --git a/src/zencore/include/zencore/iobuffer.h b/src/zencore/include/zencore/iobuffer.h
index 15455dbaa..493b7375e 100644
--- a/src/zencore/include/zencore/iobuffer.h
+++ b/src/zencore/include/zencore/iobuffer.h
@@ -243,7 +243,6 @@ protected:
kIsMutable = 1 << 1,
kIsExtended = 1 << 2, // Is actually a SharedBufferExtendedCore
kIsMaterialized = 1 << 3, // Data pointers are valid
- kLowLevelAlloc = 1 << 4, // Using direct memory allocation
kIsWholeFile = 1 << 5, // References an entire file
kIoBufferAlloc = 1 << 6, // Using IoBuffer allocator
kIsOwnedByThis = 1 << 7,
diff --git a/src/zencore/include/zencore/memory.h b/src/zencore/include/zencore/memory.h
index 7a893d3ab..fdea1a5f1 100644
--- a/src/zencore/include/zencore/memory.h
+++ b/src/zencore/include/zencore/memory.h
@@ -24,18 +24,6 @@ concept ContiguousRange = true;
struct MemoryView;
-class MemoryArena
-{
-public:
- ZENCORE_API MemoryArena();
- ZENCORE_API ~MemoryArena();
-
- ZENCORE_API void* Alloc(size_t Size, size_t Alignment);
- ZENCORE_API void Free(void* Ptr);
-
-private:
-};
-
class Memory
{
public:
@@ -43,35 +31,6 @@ public:
ZENCORE_API static void Free(void* Ptr);
};
-/** Allocator which claims fixed-size blocks from the underlying allocator.
-
- There is no way to free individual memory blocks.
-
- \note This is not thread-safe, you will need to provide synchronization yourself
-*/
-
-class ChunkingLinearAllocator
-{
-public:
- explicit ChunkingLinearAllocator(uint64_t ChunkSize, uint64_t ChunkAlignment = sizeof(std::max_align_t));
- ~ChunkingLinearAllocator();
-
- ZENCORE_API void Reset();
-
- ZENCORE_API void* Alloc(size_t Size, size_t Alignment = sizeof(void*));
- inline void Free(void* Ptr) { ZEN_UNUSED(Ptr); /* no-op */ }
-
- ChunkingLinearAllocator(const ChunkingLinearAllocator&) = delete;
- ChunkingLinearAllocator& operator=(const ChunkingLinearAllocator&) = delete;
-
-private:
- uint8_t* m_ChunkCursor = nullptr;
- uint64_t m_ChunkBytesRemain = 0;
- const uint64_t m_ChunkSize = 0;
- const uint64_t m_ChunkAlignment = 0;
- std::vector<void*> m_ChunkList;
-};
-
//////////////////////////////////////////////////////////////////////////
struct MutableMemoryView
diff --git a/src/zencore/include/zencore/memory/align.h b/src/zencore/include/zencore/memory/align.h
new file mode 100644
index 000000000..acf4157c4
--- /dev/null
+++ b/src/zencore/include/zencore/memory/align.h
@@ -0,0 +1,67 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zenbase/zenbase.h>
+
+namespace zen {
+
+/**
+ * Aligns a value to the nearest higher multiple of 'Alignment', which must be a power of two.
+ *
+ * @param Val The value to align.
+ * @param Alignment The alignment value, must be a power of two.
+ *
+ * @return The value aligned up to the specified alignment.
+ */
+template<typename T>
+constexpr T
+Align(T Val, uint64_t Alignment)
+{
+ return (T)(((uint64_t)Val + Alignment - 1) & ~(Alignment - 1));
+}
+
+/**
+ * Aligns a value to the nearest lower multiple of 'Alignment', which must be a power of two.
+ *
+ * @param Val The value to align.
+ * @param Alignment The alignment value, must be a power of two.
+ *
+ * @return The value aligned down to the specified alignment.
+ */
+template<typename T>
+constexpr T
+AlignDown(T Val, uint64_t Alignment)
+{
+ return (T)(((uint64_t)Val) & ~(Alignment - 1));
+}
+
+/**
+ * Checks if a pointer is aligned to the specified alignment.
+ *
+ * @param Val The value to align.
+ * @param Alignment The alignment value, must be a power of two.
+ *
+ * @return true if the pointer is aligned to the specified alignment, false otherwise.
+ */
+template<typename T>
+constexpr bool
+IsAligned(T Val, uint64_t Alignment)
+{
+ return !((uint64_t)Val & (Alignment - 1));
+}
+
+/**
+ * Aligns a value to the nearest higher multiple of 'Alignment'.
+ *
+ * @param Val The value to align.
+ * @param Alignment The alignment value, can be any arbitrary value.
+ *
+ * @return The value aligned up to the specified alignment.
+ */
+template<typename T>
+constexpr T
+AlignArbitrary(T Val, uint64_t Alignment)
+{
+ return (T)((((uint64_t)Val + Alignment - 1) / Alignment) * Alignment);
+}
+
+} // namespace zen
diff --git a/src/zencore/include/zencore/string.h b/src/zencore/include/zencore/string.h
index b0232d883..b10b6a2ba 100644
--- a/src/zencore/include/zencore/string.h
+++ b/src/zencore/include/zencore/string.h
@@ -51,6 +51,12 @@ StringLength(const wchar_t* str)
return wcslen(str);
}
+inline size_t
+StringLength(const char16_t* str)
+{
+ return std::char_traits<char16_t>::length(str);
+}
+
//////////////////////////////////////////////////////////////////////////
// File name helpers
//
diff --git a/src/zencore/iobuffer.cpp b/src/zencore/iobuffer.cpp
index 604cd90d6..51f380c34 100644
--- a/src/zencore/iobuffer.cpp
+++ b/src/zencore/iobuffer.cpp
@@ -43,20 +43,6 @@ namespace zen {
void
IoBufferCore::AllocateBuffer(size_t InSize, size_t Alignment) const
{
-#if ZEN_PLATFORM_WINDOWS
- if (((InSize & 0xffFF) == 0) && (Alignment == 0x10000))
- {
- m_Flags.fetch_or(kLowLevelAlloc, std::memory_order_relaxed);
- void* Ptr = VirtualAlloc(nullptr, InSize, MEM_COMMIT, PAGE_READWRITE);
- if (!Ptr)
- {
- ThrowLastError(fmt::format("VirtualAlloc failed for {:#x} bytes aligned to {:#x}", InSize, Alignment));
- }
- m_DataPtr = Ptr;
- return;
- }
-#endif // ZEN_PLATFORM_WINDOWS
-
#if ZEN_USE_MIMALLOC
void* Ptr = mi_aligned_alloc(Alignment, RoundUp(InSize, Alignment));
m_Flags.fetch_or(kIoBufferAlloc, std::memory_order_relaxed);
@@ -80,14 +66,6 @@ IoBufferCore::FreeBuffer()
}
const uint32_t LocalFlags = m_Flags.load(std::memory_order_relaxed);
-#if ZEN_PLATFORM_WINDOWS
- if (LocalFlags & kLowLevelAlloc)
- {
- VirtualFree(const_cast<void*>(m_DataPtr), 0, MEM_DECOMMIT);
-
- return;
- }
-#endif // ZEN_PLATFORM_WINDOWS
#if ZEN_USE_MIMALLOC
if (LocalFlags & kIoBufferAlloc)
diff --git a/src/zencore/memory.cpp b/src/zencore/memory.cpp
index 808c9fcb6..a0d911786 100644
--- a/src/zencore/memory.cpp
+++ b/src/zencore/memory.cpp
@@ -52,28 +52,6 @@ AlignedFreeImpl(void* ptr)
//////////////////////////////////////////////////////////////////////////
-MemoryArena::MemoryArena()
-{
-}
-
-MemoryArena::~MemoryArena()
-{
-}
-
-void*
-MemoryArena::Alloc(size_t Size, size_t Alignment)
-{
- return AlignedAllocImpl(Size, Alignment);
-}
-
-void
-MemoryArena::Free(void* ptr)
-{
- AlignedFreeImpl(ptr);
-}
-
-//////////////////////////////////////////////////////////////////////////
-
void*
Memory::Alloc(size_t Size, size_t Alignment)
{
@@ -87,105 +65,12 @@ Memory::Free(void* ptr)
}
//////////////////////////////////////////////////////////////////////////
-
-ChunkingLinearAllocator::ChunkingLinearAllocator(uint64_t ChunkSize, uint64_t ChunkAlignment)
-: m_ChunkSize(ChunkSize)
-, m_ChunkAlignment(ChunkAlignment)
-{
-}
-
-ChunkingLinearAllocator::~ChunkingLinearAllocator()
-{
- Reset();
-}
-
-void
-ChunkingLinearAllocator::Reset()
-{
- for (void* ChunkEntry : m_ChunkList)
- {
- Memory::Free(ChunkEntry);
- }
- m_ChunkList.clear();
-
- m_ChunkCursor = nullptr;
- m_ChunkBytesRemain = 0;
-}
-
-void*
-ChunkingLinearAllocator::Alloc(size_t Size, size_t Alignment)
-{
- ZEN_ASSERT_SLOW(zen::IsPow2(Alignment));
-
- // This could be improved in a bunch of ways
- //
- // * We pessimistically allocate memory even though there may be enough memory available for a single allocation due to the way we take
- // alignment into account below
- // * The block allocation size could be chosen to minimize slack for the case when multiple oversize allocations are made rather than
- // minimizing the number of chunks
- // * ...
-
- const uint64_t AllocationSize = zen::RoundUp(Size, Alignment);
-
- if (m_ChunkBytesRemain < (AllocationSize + Alignment - 1))
- {
- const uint64_t ChunkSize = zen::RoundUp(zen::Max(m_ChunkSize, Size), m_ChunkSize);
- void* ChunkPtr = Memory::Alloc(ChunkSize, m_ChunkAlignment);
- if (!ChunkPtr)
- {
- ThrowOutOfMemory(fmt::format("failed allocating {:#x} bytes aligned to {:#x}", ChunkSize, m_ChunkAlignment));
- }
- m_ChunkCursor = reinterpret_cast<uint8_t*>(ChunkPtr);
- m_ChunkBytesRemain = ChunkSize;
- m_ChunkList.push_back(ChunkPtr);
- }
-
- const uint64_t AlignFixup = (Alignment - reinterpret_cast<uintptr_t>(m_ChunkCursor)) & (Alignment - 1);
- void* ReturnPtr = m_ChunkCursor + AlignFixup;
- const uint64_t Delta = AlignFixup + AllocationSize;
-
- ZEN_ASSERT_SLOW(m_ChunkBytesRemain >= Delta);
-
- m_ChunkCursor += Delta;
- m_ChunkBytesRemain -= Delta;
-
- ZEN_ASSERT_SLOW(IsPointerAligned(ReturnPtr, Alignment));
-
- return ReturnPtr;
-}
-
-//////////////////////////////////////////////////////////////////////////
//
// Unit tests
//
#if ZEN_WITH_TESTS
-TEST_CASE("ChunkingLinearAllocator")
-{
- ChunkingLinearAllocator Allocator(4096);
-
- void* p1 = Allocator.Alloc(1, 1);
- void* p2 = Allocator.Alloc(1, 1);
-
- CHECK(p1 != p2);
-
- void* p3 = Allocator.Alloc(1, 4);
- CHECK(IsPointerAligned(p3, 4));
-
- void* p3_2 = Allocator.Alloc(1, 4);
- CHECK(IsPointerAligned(p3_2, 4));
-
- void* p4 = Allocator.Alloc(1, 8);
- CHECK(IsPointerAligned(p4, 8));
-
- for (int i = 0; i < 100; ++i)
- {
- void* p0 = Allocator.Alloc(64);
- ZEN_UNUSED(p0);
- }
-}
-
TEST_CASE("MemoryView")
{
{
diff --git a/src/zencore/thread.cpp b/src/zencore/thread.cpp
index ca3759fbf..78afe924b 100644
--- a/src/zencore/thread.cpp
+++ b/src/zencore/thread.cpp
@@ -80,8 +80,10 @@ SetNameInternal(DWORD thread_id, const char* name)
void
SetCurrentThreadName([[maybe_unused]] std::string_view ThreadName)
{
- std::string ThreadNameZ{ThreadName};
- const int ThreadId = GetCurrentThreadId();
+ StringBuilder<256> ThreadNameZ;
+ ThreadNameZ << ThreadName;
+ const int ThreadId = GetCurrentThreadId();
+
#if ZEN_WITH_TRACE
trace::ThreadRegister(ThreadNameZ.c_str(), /* system id */ ThreadId, /* sort id */ 0);
#endif // ZEN_WITH_TRACE