diff options
| author | Stefan Boberg <[email protected]> | 2024-11-19 09:59:04 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2024-11-19 09:59:04 +0100 |
| commit | e335ca10a6c6a1e37b155e2155f5c5908c0272ae (patch) | |
| tree | 78b32384df0e58b22052dcfa7990c16921c6b650 /src | |
| parent | 5.5.12 (diff) | |
| download | zen-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.cpp | 72 | ||||
| -rw-r--r-- | src/zencore/include/zencore/commandline.h | 39 | ||||
| -rw-r--r-- | src/zencore/include/zencore/iobuffer.h | 1 | ||||
| -rw-r--r-- | src/zencore/include/zencore/memory.h | 41 | ||||
| -rw-r--r-- | src/zencore/include/zencore/memory/align.h | 67 | ||||
| -rw-r--r-- | src/zencore/include/zencore/string.h | 6 | ||||
| -rw-r--r-- | src/zencore/iobuffer.cpp | 22 | ||||
| -rw-r--r-- | src/zencore/memory.cpp | 115 | ||||
| -rw-r--r-- | src/zencore/thread.cpp | 6 |
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 |