170 lines
4.0 KiB
C++
170 lines
4.0 KiB
C++
|
#define SOL_ALL_SAFETIES_ON 1
|
||
|
#include <sol/sol.hpp>
|
||
|
|
||
|
#include <cstddef>
|
||
|
#include <cstdlib>
|
||
|
|
||
|
class memory_tracker {
|
||
|
public:
|
||
|
// 10 MB or something?
|
||
|
// idk whatever
|
||
|
inline static constexpr std::size_t arbitrary_default_limit
|
||
|
= 1024 * 1024 * 10;
|
||
|
|
||
|
memory_tracker() : memory_tracker(arbitrary_default_limit) {
|
||
|
}
|
||
|
|
||
|
memory_tracker(std::size_t maximum_memory)
|
||
|
: used(0)
|
||
|
, limit(maximum_memory)
|
||
|
, n_threads(0)
|
||
|
, n_tables(0)
|
||
|
, n_functions(0)
|
||
|
, n_userdata(0)
|
||
|
, n_strings(0) {
|
||
|
}
|
||
|
|
||
|
std::size_t currently_used() const {
|
||
|
return used;
|
||
|
}
|
||
|
|
||
|
std::size_t memory_limit() const {
|
||
|
return limit;
|
||
|
}
|
||
|
|
||
|
static void* allocate(void* memory_tracker_ud, void* ptr,
|
||
|
size_t object_code, size_t nsize) {
|
||
|
memory_tracker& self = (*static_cast<memory_tracker*>(
|
||
|
memory_tracker_ud));
|
||
|
return self.alloc(ptr, object_code, nsize);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
void* alloc(void* ptr, size_t original_block_size_or_code,
|
||
|
size_t new_block_size) {
|
||
|
std::size_t original_block_size
|
||
|
= original_block_size_or_code;
|
||
|
if (ptr == nullptr) {
|
||
|
// object code!
|
||
|
sol::type object_type = static_cast<sol::type>(
|
||
|
original_block_size_or_code);
|
||
|
switch (object_type) {
|
||
|
case sol::type::function:
|
||
|
++n_functions;
|
||
|
break;
|
||
|
case sol::type::string:
|
||
|
++n_strings;
|
||
|
break;
|
||
|
case sol::type::userdata:
|
||
|
++n_userdata;
|
||
|
break;
|
||
|
case sol::type::table:
|
||
|
++n_tables;
|
||
|
break;
|
||
|
case sol::type::thread:
|
||
|
++n_threads;
|
||
|
break;
|
||
|
default:
|
||
|
// not a clue, fam
|
||
|
break;
|
||
|
}
|
||
|
// because it is an object code,
|
||
|
// it tells us literally nothing about
|
||
|
// the old block size,
|
||
|
// so set that to 0
|
||
|
original_block_size = 0;
|
||
|
}
|
||
|
if (new_block_size == 0) {
|
||
|
// Lua expects us to act like a "free"
|
||
|
// when the new block size is 0
|
||
|
std::free(ptr);
|
||
|
used -= original_block_size;
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
// did we hit the limit?
|
||
|
std::size_t memory_differntial
|
||
|
= new_block_size - original_block_size;
|
||
|
std::size_t desired_use = used + memory_differntial;
|
||
|
if (desired_use > limit) {
|
||
|
// tell the Lua Virtual Machine
|
||
|
// to toss off (by returning nullptr)
|
||
|
return nullptr;
|
||
|
}
|
||
|
// alright now we have to expand this shit
|
||
|
// guess we use C's realloc
|
||
|
ptr = std::realloc(ptr, new_block_size);
|
||
|
if (ptr != nullptr) {
|
||
|
// alright, we successfully allocated some space
|
||
|
// track it
|
||
|
used = desired_use;
|
||
|
}
|
||
|
// even if we're null, let Lua crash and burn for us
|
||
|
return ptr;
|
||
|
}
|
||
|
|
||
|
std::size_t used;
|
||
|
std::size_t limit;
|
||
|
std::size_t n_threads;
|
||
|
std::size_t n_tables;
|
||
|
std::size_t n_functions;
|
||
|
std::size_t n_userdata;
|
||
|
std::size_t n_strings;
|
||
|
};
|
||
|
|
||
|
struct my_type {
|
||
|
int a = 24;
|
||
|
bool b = false;
|
||
|
double d = 3.5;
|
||
|
};
|
||
|
|
||
|
#include <iostream>
|
||
|
|
||
|
int main() {
|
||
|
|
||
|
std::cout << "=== memory tracker ===" << std::endl;
|
||
|
|
||
|
#if SOL_LUAJIT_VERSION < 20100 && (UINTPTR_MAX > 0xFFFFFFFF)
|
||
|
std::cout << "LuaJIT in x64 mode on LuaJIT 2.0.X versions "
|
||
|
"does not support using a custom allocator!"
|
||
|
<< std::endl;
|
||
|
#else
|
||
|
memory_tracker box;
|
||
|
std::cout << "memory at start: " << box.currently_used()
|
||
|
<< " bytes / " << box.memory_limit() << " bytes"
|
||
|
<< std::endl;
|
||
|
sol::state lua(&sol::default_at_panic,
|
||
|
&memory_tracker::allocate,
|
||
|
&box);
|
||
|
lua.open_libraries(sol::lib::base);
|
||
|
|
||
|
std::cout << "memory after state creation: "
|
||
|
<< box.currently_used() << " bytes / "
|
||
|
<< box.memory_limit() << " bytes" << std::endl;
|
||
|
|
||
|
// trigger some allocations
|
||
|
lua.new_usertype<my_type>("my_type",
|
||
|
"a",
|
||
|
&my_type::a,
|
||
|
"b",
|
||
|
&my_type::b,
|
||
|
"d",
|
||
|
&my_type::d);
|
||
|
lua["f"] = [](std::string s) { return s + " woof!"; };
|
||
|
|
||
|
std::cout << "memory after function and usertype setup: "
|
||
|
<< box.currently_used() << " bytes / "
|
||
|
<< box.memory_limit() << " bytes" << std::endl;
|
||
|
|
||
|
lua.safe_script("print(f('bark'))");
|
||
|
lua.safe_script(
|
||
|
"local obj = my_type.new() print(obj.a, obj.b, obj.c) "
|
||
|
"obj.b = true print(obj.a, obj.b, obj.c)");
|
||
|
|
||
|
std::cout << "memory at end: " << box.currently_used()
|
||
|
<< " bytes / " << box.memory_limit() << " bytes"
|
||
|
<< std::endl;
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|