sol2/examples/source/allocation_limit.cpp

136 lines
3.9 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;
}