[String] Make VA leak-safe
This commit is contained in:
parent
8785dfe316
commit
d8c3e7fcee
@ -6,7 +6,6 @@ namespace Components
|
|||||||
bool Loader::Postgame = false;
|
bool Loader::Postgame = false;
|
||||||
bool Loader::ComInitialized = false;
|
bool Loader::ComInitialized = false;
|
||||||
std::vector<Component*> Loader::Components;
|
std::vector<Component*> Loader::Components;
|
||||||
Utils::Memory::Allocator Loader::MemAllocator;
|
|
||||||
|
|
||||||
bool Loader::IsPregame()
|
bool Loader::IsPregame()
|
||||||
{
|
{
|
||||||
@ -27,7 +26,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Loader::Pregame = true;
|
Loader::Pregame = true;
|
||||||
Loader::Postgame = false;
|
Loader::Postgame = false;
|
||||||
Loader::MemAllocator.clear();
|
Utils::Memory::GetAllocator()->clear();
|
||||||
|
|
||||||
Loader::ComInitialized = false;
|
Loader::ComInitialized = false;
|
||||||
if (!Loader::PerformingUnitTests() && !Utils::IsWineEnvironment()) Loader::ComInitialized = (CoInitialize(nullptr) == S_OK);
|
if (!Loader::PerformingUnitTests() && !Utils::IsWineEnvironment()) Loader::ComInitialized = (CoInitialize(nullptr) == S_OK);
|
||||||
@ -122,7 +121,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
Loader::Components.clear();
|
Loader::Components.clear();
|
||||||
Loader::MemAllocator.clear();
|
Utils::Memory::GetAllocator()->clear();
|
||||||
|
|
||||||
if (!Loader::PerformingUnitTests() && !Utils::IsWineEnvironment() && Loader::ComInitialized) CoUninitialize();
|
if (!Loader::PerformingUnitTests() && !Utils::IsWineEnvironment() && Loader::ComInitialized) CoUninitialize();
|
||||||
}
|
}
|
||||||
@ -202,9 +201,4 @@ namespace Components
|
|||||||
Loader::Components.push_back(component);
|
Loader::Components.push_back(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Memory::Allocator* Loader::GetAlloctor()
|
|
||||||
{
|
|
||||||
return &Loader::MemAllocator;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -53,14 +53,11 @@ namespace Components
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Utils::Memory::Allocator* GetAlloctor();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool Pregame;
|
static bool Pregame;
|
||||||
static bool Postgame;
|
static bool Postgame;
|
||||||
static bool ComInitialized;
|
static bool ComInitialized;
|
||||||
static std::vector<Component*> Components;
|
static std::vector<Component*> Components;
|
||||||
static Utils::Memory::Allocator MemAllocator;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +72,8 @@ namespace Components
|
|||||||
std::uint8_t prefix = (request->tableRow >> (8 * 3)) & 0xFF;
|
std::uint8_t prefix = (request->tableRow >> (8 * 3)) & 0xFF;
|
||||||
std::uint8_t data = (request->tableRow >> (8 * 2)) & 0xFF;
|
std::uint8_t data = (request->tableRow >> (8 * 2)) & 0xFF;
|
||||||
|
|
||||||
|
if (data >= ARRAYSIZE(CardTitles::CustomTitles)) return nullptr;
|
||||||
|
|
||||||
if (request->tablename == "mp/cardTitleTable.csv"s)
|
if (request->tablename == "mp/cardTitleTable.csv"s)
|
||||||
{
|
{
|
||||||
if (prefix != 0x00)
|
if (prefix != 0x00)
|
||||||
@ -81,10 +83,10 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (prefix == 0xFE)
|
if (prefix == 0xFE)
|
||||||
{
|
{
|
||||||
if (!CustomTitleDvar.get<std::string>().empty())
|
if (!CardTitles::CustomTitleDvar.get<std::string>().empty())
|
||||||
{
|
{
|
||||||
// 0xFF in front of the title to skip localization. Or else it will wait for a couple of seconds for the asset of type localize
|
// 0xFF in front of the title to skip localization. Or else it will wait for a couple of seconds for the asset of type localize
|
||||||
const char* title = Utils::String::VA("\x15%s", CustomTitleDvar.get<const char*>());
|
const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitleDvar.get<const char*>());
|
||||||
|
|
||||||
// prepare return value
|
// prepare return value
|
||||||
operand->internals.stringVal.string = title;
|
operand->internals.stringVal.string = title;
|
||||||
@ -95,9 +97,9 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else if (prefix == 0xFF)
|
else if (prefix == 0xFF)
|
||||||
{
|
{
|
||||||
if (!CustomTitles[data].empty())
|
if (!CardTitles::CustomTitles[data].empty())
|
||||||
{
|
{
|
||||||
const char* title = Utils::String::VA("\x15%s", CustomTitles[data].data());
|
const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitles[data].data());
|
||||||
|
|
||||||
// prepare return value
|
// prepare return value
|
||||||
operand->internals.stringVal.string = title;
|
operand->internals.stringVal.string = title;
|
||||||
|
@ -129,7 +129,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::cmd_function_t* Command::Allocate()
|
Game::cmd_function_t* Command::Allocate()
|
||||||
{
|
{
|
||||||
return Loader::GetAlloctor()->allocate<Game::cmd_function_t>();
|
return Utils::Memory::GetAllocator()->allocate<Game::cmd_function_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command::MainCallback()
|
void Command::MainCallback()
|
||||||
|
@ -10,7 +10,7 @@ namespace Components
|
|||||||
void Localization::Set(std::string key, std::string value)
|
void Localization::Set(std::string key, std::string value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
if (Localization::LocalizeMap.find(key) != Localization::LocalizeMap.end())
|
if (Localization::LocalizeMap.find(key) != Localization::LocalizeMap.end())
|
||||||
{
|
{
|
||||||
@ -76,7 +76,7 @@ namespace Components
|
|||||||
void Localization::SetTemp(std::string key, std::string value)
|
void Localization::SetTemp(std::string key, std::string value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
if (Localization::TempLocalizeMap.find(key) != Localization::TempLocalizeMap.end())
|
if (Localization::TempLocalizeMap.find(key) != Localization::TempLocalizeMap.end())
|
||||||
{
|
{
|
||||||
@ -111,7 +111,7 @@ namespace Components
|
|||||||
void Localization::ClearTemp()
|
void Localization::ClearTemp()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
for (auto i = Localization::TempLocalizeMap.begin(); i != Localization::TempLocalizeMap.end(); ++i)
|
for (auto i = Localization::TempLocalizeMap.begin(); i != Localization::TempLocalizeMap.end(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -55,7 +55,7 @@ namespace Components
|
|||||||
|
|
||||||
int Menus::LoadMenuSource(std::string name, std::string buffer)
|
int Menus::LoadMenuSource(std::string name, std::string buffer)
|
||||||
{
|
{
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
int handle = Menus::ReserveSourceHandle();
|
int handle = Menus::ReserveSourceHandle();
|
||||||
if (!Menus::IsValidSourceHandle(handle)) return 0; // No free source slot!
|
if (!Menus::IsValidSourceHandle(handle)) return 0; // No free source slot!
|
||||||
@ -114,7 +114,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::menuDef_t* Menus::ParseMenu(int handle)
|
Game::menuDef_t* Menus::ParseMenu(int handle)
|
||||||
{
|
{
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
Game::menuDef_t* menu = allocator->allocate<Game::menuDef_t>();
|
Game::menuDef_t* menu = allocator->allocate<Game::menuDef_t>();
|
||||||
if (!menu) return nullptr;
|
if (!menu) return nullptr;
|
||||||
@ -239,7 +239,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::MenuList* Menus::LoadScriptMenu(const char* menu)
|
Game::MenuList* Menus::LoadScriptMenu(const char* menu)
|
||||||
{
|
{
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
std::vector<std::pair<bool, Game::menuDef_t*>> menus = Menus::LoadMenu(menu);
|
std::vector<std::pair<bool, Game::menuDef_t*>> menus = Menus::LoadMenu(menu);
|
||||||
if (menus.empty()) return nullptr;
|
if (menus.empty()) return nullptr;
|
||||||
@ -318,7 +318,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::MenuList* Menus::LoadMenuList(Game::MenuList* menuList)
|
Game::MenuList* Menus::LoadMenuList(Game::MenuList* menuList)
|
||||||
{
|
{
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
std::vector<std::pair<bool, Game::menuDef_t*>> menus;
|
std::vector<std::pair<bool, Game::menuDef_t*>> menus;
|
||||||
|
|
||||||
@ -376,7 +376,7 @@ namespace Components
|
|||||||
|
|
||||||
void Menus::FreeMenuSource(int handle)
|
void Menus::FreeMenuSource(int handle)
|
||||||
{
|
{
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
if (!Menus::IsValidSourceHandle(handle)) return;
|
if (!Menus::IsValidSourceHandle(handle)) return;
|
||||||
|
|
||||||
@ -419,7 +419,7 @@ namespace Components
|
|||||||
|
|
||||||
void Menus::FreeMenu(Game::menuDef_t* menudef)
|
void Menus::FreeMenu(Game::menuDef_t* menudef)
|
||||||
{
|
{
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
// Do i need to free expressions and strings?
|
// Do i need to free expressions and strings?
|
||||||
// Or does the game take care of it?
|
// Or does the game take care of it?
|
||||||
@ -444,7 +444,7 @@ namespace Components
|
|||||||
void Menus::FreeMenuList(Game::MenuList* menuList)
|
void Menus::FreeMenuList(Game::MenuList* menuList)
|
||||||
{
|
{
|
||||||
if (!menuList) return;
|
if (!menuList) return;
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
// Keep our compiler happy
|
// Keep our compiler happy
|
||||||
Game::MenuList list = { menuList->name, menuList->menuCount, menuList->menus };
|
Game::MenuList list = { menuList->name, menuList->menuCount, menuList->menus };
|
||||||
|
@ -20,7 +20,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::StringTable* StringTable::LoadObject(std::string filename)
|
Game::StringTable* StringTable::LoadObject(std::string filename)
|
||||||
{
|
{
|
||||||
Utils::Memory::Allocator* allocator = Loader::GetAlloctor();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
filename = Utils::String::ToLower(filename);
|
filename = Utils::String::ToLower(filename);
|
||||||
|
|
||||||
|
@ -921,7 +921,7 @@ namespace Components
|
|||||||
|
|
||||||
void ZoneBuilder::HandleError(int level, const char* format, ...)
|
void ZoneBuilder::HandleError(int level, const char* format, ...)
|
||||||
{
|
{
|
||||||
char buffer[256];
|
char buffer[256] = { 0 };
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
vsnprintf_s(buffer, 256, format, args);
|
vsnprintf_s(buffer, 256, format, args);
|
||||||
|
@ -388,7 +388,7 @@ namespace Game
|
|||||||
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
|
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
|
||||||
{
|
{
|
||||||
int elSize = DB_GetXAssetSizeHandlers[type]();
|
int elSize = DB_GetXAssetSizeHandlers[type]();
|
||||||
XAssetHeader poolEntry = { Components::Loader::GetAlloctor()->allocate(newSize * elSize) };
|
XAssetHeader poolEntry = { Utils::Memory::GetAllocator()->allocate(newSize * elSize) };
|
||||||
DB_XAssetPool[type] = poolEntry;
|
DB_XAssetPool[type] = poolEntry;
|
||||||
g_poolSize[type] = newSize;
|
g_poolSize[type] = newSize;
|
||||||
return poolEntry;
|
return poolEntry;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
|
||||||
// Requires Visual Leak Detector plugin: http://vld.codeplex.com/
|
// Requires Visual Leak Detector plugin: http://vld.codeplex.com/
|
||||||
|
#define VLD_FORCE_ENABLE
|
||||||
//#include <vld.h>
|
//#include <vld.h>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
|
Utils::Memory::Allocator Memory::MemAllocator;
|
||||||
|
|
||||||
void* Memory::AllocateAlign(size_t length, size_t alignment)
|
void* Memory::AllocateAlign(size_t length, size_t alignment)
|
||||||
{
|
{
|
||||||
void* data = _aligned_malloc(length, alignment);
|
void* data = _aligned_malloc(length, alignment);
|
||||||
assert(data != nullptr);
|
assert(data != nullptr);
|
||||||
ZeroMemory(data, length);
|
if(data) ZeroMemory(data, length);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,4 +97,9 @@ namespace Utils
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Utils::Memory::Allocator* Memory::GetAllocator()
|
||||||
|
{
|
||||||
|
return &Memory::MemAllocator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,5 +154,10 @@ namespace Utils
|
|||||||
|
|
||||||
static bool IsBadReadPtr(const void* ptr);
|
static bool IsBadReadPtr(const void* ptr);
|
||||||
static bool IsBadCodePtr(const void* ptr);
|
static bool IsBadCodePtr(const void* ptr);
|
||||||
|
|
||||||
|
static Utils::Memory::Allocator* GetAllocator();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Utils::Memory::Allocator MemAllocator;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,41 +7,9 @@ namespace Utils
|
|||||||
{
|
{
|
||||||
namespace String
|
namespace String
|
||||||
{
|
{
|
||||||
VAProvider::VAProvider(size_t buffers) : currentBuffer(0)
|
|
||||||
{
|
|
||||||
this->stringBuffers.resize(buffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
char* VAProvider::get(const char* format, va_list ap)
|
|
||||||
{
|
|
||||||
++this->currentBuffer %= this->stringBuffers.size();
|
|
||||||
auto& buffer = this->stringBuffers[this->currentBuffer];
|
|
||||||
|
|
||||||
if (!buffer.first || !buffer.second)
|
|
||||||
{
|
|
||||||
buffer.first = 256;
|
|
||||||
if (buffer.second) this->allocator.free(buffer.second);
|
|
||||||
buffer.second = this->allocator.allocateArray<char>(buffer.first + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
int res = vsnprintf_s(buffer.second, buffer.first, _TRUNCATE, format, ap);
|
|
||||||
if (res > 0) break; // Success
|
|
||||||
if (res == 0) return ""; // Error
|
|
||||||
|
|
||||||
buffer.first *= 2;
|
|
||||||
|
|
||||||
this->allocator.free(buffer.second);
|
|
||||||
buffer.second = this->allocator.allocateArray<char>(buffer.first + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *VA(const char *fmt, ...)
|
const char *VA(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
static thread_local VAProvider provider;
|
static VAProvider<8, 256> provider;
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
|
@ -4,18 +4,89 @@ namespace Utils
|
|||||||
{
|
{
|
||||||
namespace String
|
namespace String
|
||||||
{
|
{
|
||||||
|
template <size_t Buffers, size_t MinBufferSize>
|
||||||
class VAProvider
|
class VAProvider
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VAProvider(size_t buffers = 8);
|
typename std::enable_if<(Buffers != 0 && MinBufferSize != 0), char*>::type
|
||||||
|
get(const char* format, va_list ap)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> _(this->accessMutex);
|
||||||
|
|
||||||
char* get(const char* format, va_list ap);
|
auto threadBuffers = this->stringBuffers.find(std::this_thread::get_id());
|
||||||
|
if (threadBuffers == this->stringBuffers.end())
|
||||||
|
{
|
||||||
|
this->stringBuffers[std::this_thread::get_id()] = Pool();
|
||||||
|
threadBuffers = this->stringBuffers.find(std::this_thread::get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!threadBuffers->second.stringPool.size()) threadBuffers->second.stringPool.resize(Buffers);
|
||||||
|
|
||||||
|
++threadBuffers->second.currentBuffer %= threadBuffers->second.stringPool.size();
|
||||||
|
auto& entry = threadBuffers->second.stringPool[threadBuffers->second.currentBuffer];
|
||||||
|
|
||||||
|
if (!entry.size || !entry.buffer)
|
||||||
|
{
|
||||||
|
entry = Entry(MinBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int res = vsnprintf_s(entry.buffer, entry.size, _TRUNCATE, format, ap);
|
||||||
|
if (res > 0) break; // Success
|
||||||
|
if (res == 0) return ""; // Error
|
||||||
|
|
||||||
|
entry.doubleSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t currentBuffer;
|
class Entry
|
||||||
std::vector<std::pair<size_t,char*>> stringBuffers;
|
{
|
||||||
|
public:
|
||||||
|
Entry(size_t _size = MinBufferSize) : size(_size), buffer(nullptr)
|
||||||
|
{
|
||||||
|
if (this->size < MinBufferSize) this->size = MinBufferSize;
|
||||||
|
this->allocate();
|
||||||
|
}
|
||||||
|
|
||||||
Utils::Memory::Allocator allocator;
|
~Entry()
|
||||||
|
{
|
||||||
|
if(this->buffer) Utils::Memory::GetAllocator()->free(this->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void allocate()
|
||||||
|
{
|
||||||
|
if (this->buffer) Utils::Memory::GetAllocator()->free(this->buffer);
|
||||||
|
this->buffer = Utils::Memory::GetAllocator()->allocateArray<char>(this->size + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doubleSize()
|
||||||
|
{
|
||||||
|
this->size *= 2;
|
||||||
|
this->allocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size;
|
||||||
|
char* buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Pool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pool() : currentBuffer(0)
|
||||||
|
{
|
||||||
|
this->stringPool.resize(Buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t currentBuffer;
|
||||||
|
std::vector<Entry> stringPool;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::mutex accessMutex;
|
||||||
|
std::unordered_map<std::thread::id, Pool> stringBuffers;
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *VA(const char *fmt, ...);
|
const char *VA(const char *fmt, ...);
|
||||||
|
Loading…
Reference in New Issue
Block a user