mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
Fix required memory size calculations, do only one pass.
This commit is contained in:
parent
1a7c9fc74b
commit
05235646ef
|
@ -94,16 +94,35 @@ namespace sol {
|
||||||
return reinterpret_cast<void*>(align(alignment, reinterpret_cast<std::uintptr_t>(ptr), space));
|
return reinterpret_cast<void*>(align(alignment, reinterpret_cast<std::uintptr_t>(ptr), space));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::uintptr_t align_one(std::size_t alignment, std::size_t size, std::uintptr_t target_alignment) {
|
constexpr std::uintptr_t align_one(std::size_t alignment, std::size_t size, std::uintptr_t ptr) {
|
||||||
std::size_t space = (std::numeric_limits<std::size_t>::max)();
|
std::size_t space = (std::numeric_limits<std::size_t>::max)();
|
||||||
return align(alignment, target_alignment, space) + size;
|
return align(alignment, ptr, space) + size;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
constexpr std::size_t aligned_space_for(std::uintptr_t alignment) {
|
constexpr std::size_t aligned_space_for(std::uintptr_t ptr) {
|
||||||
std::uintptr_t start = alignment;
|
std::uintptr_t end = ptr;
|
||||||
(void)detail::swallow { int {}, (alignment = align_one(std::alignment_of_v<Args>, sizeof(Args), alignment), int {})... };
|
((end = align_one(alignof(Args), sizeof(Args), end)), ...);
|
||||||
return static_cast<std::size_t>(alignment - start);
|
return static_cast<std::size_t>(end - ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
constexpr std::size_t aligned_space_for() {
|
||||||
|
static_assert(sizeof...(Args) > 0);
|
||||||
|
|
||||||
|
constexpr std::size_t max_arg_alignment = (std::max)({ alignof(Args)... });
|
||||||
|
if constexpr (max_arg_alignment <= alignof(std::max_align_t)) {
|
||||||
|
// If all types are `good enough`, simply calculate alignment in case of the worst allocator
|
||||||
|
std::size_t worst_required_size = 0;
|
||||||
|
for (std::size_t ptr = 0; ptr < max_arg_alignment; ptr++) {
|
||||||
|
worst_required_size = std::max(worst_required_size, aligned_space_for<Args...>(ptr));
|
||||||
|
}
|
||||||
|
return worst_required_size;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// For over-aligned types let's assume that every Arg in Args starts at the worst aligned address
|
||||||
|
return (aligned_space_for<Args>(0x1) + ...);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void* align_usertype_pointer(void* ptr) {
|
inline void* align_usertype_pointer(void* ptr) {
|
||||||
|
@ -219,19 +238,11 @@ namespace sol {
|
||||||
T** pointerpointer = static_cast<T**>(alloc_newuserdata(L, sizeof(T*)));
|
T** pointerpointer = static_cast<T**>(alloc_newuserdata(L, sizeof(T*)));
|
||||||
return pointerpointer;
|
return pointerpointer;
|
||||||
}
|
}
|
||||||
constexpr std::size_t initial_size = aligned_space_for<T*>(0x0);
|
constexpr std::size_t initial_size = aligned_space_for<T*>();
|
||||||
constexpr std::size_t misaligned_size = aligned_space_for<T*>(0x1);
|
|
||||||
|
|
||||||
std::size_t allocated_size = initial_size;
|
std::size_t allocated_size = initial_size;
|
||||||
void* unadjusted = alloc_newuserdata(L, initial_size);
|
void* unadjusted = alloc_newuserdata(L, initial_size);
|
||||||
void* adjusted = align(std::alignment_of<T*>::value, unadjusted, allocated_size);
|
void* adjusted = align(std::alignment_of<T*>::value, unadjusted, allocated_size);
|
||||||
if (adjusted == nullptr) {
|
|
||||||
lua_pop(L, 1);
|
|
||||||
// what kind of absolute garbage trash allocator are we dealing with?
|
|
||||||
// whatever, add some padding in the case of MAXIMAL alignment waste...
|
|
||||||
allocated_size = misaligned_size;
|
|
||||||
unadjusted = alloc_newuserdata(L, allocated_size);
|
|
||||||
adjusted = align(std::alignment_of<T*>::value, unadjusted, allocated_size);
|
|
||||||
if (adjusted == nullptr) {
|
if (adjusted == nullptr) {
|
||||||
// trash allocator can burn in hell
|
// trash allocator can burn in hell
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
@ -239,7 +250,6 @@ namespace sol {
|
||||||
// worse job than malloc/realloc and should go read some books, yeah?");
|
// worse job than malloc/realloc and should go read some books, yeah?");
|
||||||
luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T*>().data());
|
luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T*>().data());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return static_cast<T**>(adjusted);
|
return static_cast<T**>(adjusted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,35 +326,12 @@ namespace sol {
|
||||||
return allocationtarget;
|
return allocationtarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* the assumption is that `alloc_newuserdata` -- unless someone
|
constexpr std::size_t initial_size = aligned_space_for<T*, T>();
|
||||||
passes a specific lua_Alloc that gives us bogus, un-aligned pointers
|
|
||||||
-- uses malloc, which tends to hand out more or less aligned pointers to memory
|
|
||||||
(most of the time, anyhow)
|
|
||||||
|
|
||||||
but it's not guaranteed, so we have to do a post-adjustment check and increase padding
|
|
||||||
|
|
||||||
we do this preliminarily with compile-time stuff, to see
|
|
||||||
if we strike lucky with the allocator and alignment values
|
|
||||||
|
|
||||||
otherwise, we have to re-allocate the userdata and
|
|
||||||
over-allocate some space for additional padding because
|
|
||||||
compilers are optimized for aligned reads/writes
|
|
||||||
(and clang will barf UBsan errors on us for not being aligned)
|
|
||||||
*/
|
|
||||||
constexpr std::size_t initial_size = aligned_space_for<T*, T>(0x0);
|
|
||||||
constexpr std::size_t misaligned_size = aligned_space_for<T*, T>(0x1);
|
|
||||||
|
|
||||||
void* pointer_adjusted;
|
void* pointer_adjusted;
|
||||||
void* data_adjusted;
|
void* data_adjusted;
|
||||||
bool result
|
bool result
|
||||||
= attempt_alloc(L, std::alignment_of_v<T*>, sizeof(T*), std::alignment_of_v<T>, initial_size, pointer_adjusted, data_adjusted);
|
= attempt_alloc(L, std::alignment_of_v<T*>, sizeof(T*), std::alignment_of_v<T>, initial_size, pointer_adjusted, data_adjusted);
|
||||||
if (!result) {
|
|
||||||
// we're likely to get something that fails to perform the proper allocation a second time,
|
|
||||||
// so we use the suggested_new_size bump to help us out here
|
|
||||||
pointer_adjusted = nullptr;
|
|
||||||
data_adjusted = nullptr;
|
|
||||||
result = attempt_alloc(
|
|
||||||
L, std::alignment_of_v<T*>, sizeof(T*), std::alignment_of_v<T>, misaligned_size, pointer_adjusted, data_adjusted);
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
if (pointer_adjusted == nullptr) {
|
if (pointer_adjusted == nullptr) {
|
||||||
luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle<T>().c_str());
|
luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle<T>().c_str());
|
||||||
|
@ -354,7 +341,6 @@ namespace sol {
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
T** pointerpointer = reinterpret_cast<T**>(pointer_adjusted);
|
T** pointerpointer = reinterpret_cast<T**>(pointer_adjusted);
|
||||||
T*& pointerreference = *pointerpointer;
|
T*& pointerreference = *pointerpointer;
|
||||||
|
@ -382,8 +368,7 @@ namespace sol {
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::size_t initial_size = aligned_space_for<T*, unique_destructor, unique_tag, Real>(0x0);
|
constexpr std::size_t initial_size = aligned_space_for<T*, unique_destructor, unique_tag, Real>();
|
||||||
constexpr std::size_t misaligned_size = aligned_space_for<T*, unique_destructor, unique_tag, Real>(0x1);
|
|
||||||
|
|
||||||
void* pointer_adjusted;
|
void* pointer_adjusted;
|
||||||
void* dx_adjusted;
|
void* dx_adjusted;
|
||||||
|
@ -398,22 +383,6 @@ namespace sol {
|
||||||
dx_adjusted,
|
dx_adjusted,
|
||||||
id_adjusted,
|
id_adjusted,
|
||||||
data_adjusted);
|
data_adjusted);
|
||||||
if (!result) {
|
|
||||||
// we're likely to get something that fails to perform the proper allocation a second time,
|
|
||||||
// so we use the suggested_new_size bump to help us out here
|
|
||||||
pointer_adjusted = nullptr;
|
|
||||||
dx_adjusted = nullptr;
|
|
||||||
id_adjusted = nullptr;
|
|
||||||
data_adjusted = nullptr;
|
|
||||||
result = attempt_alloc_unique(L,
|
|
||||||
std::alignment_of_v<T*>,
|
|
||||||
sizeof(T*),
|
|
||||||
std::alignment_of_v<Real>,
|
|
||||||
misaligned_size,
|
|
||||||
pointer_adjusted,
|
|
||||||
dx_adjusted,
|
|
||||||
id_adjusted,
|
|
||||||
data_adjusted);
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
if (pointer_adjusted == nullptr) {
|
if (pointer_adjusted == nullptr) {
|
||||||
luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle<T>().c_str());
|
luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle<T>().c_str());
|
||||||
|
@ -426,7 +395,6 @@ namespace sol {
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pref = static_cast<T**>(pointer_adjusted);
|
pref = static_cast<T**>(pointer_adjusted);
|
||||||
dx = static_cast<detail::unique_destructor*>(dx_adjusted);
|
dx = static_cast<detail::unique_destructor*>(dx_adjusted);
|
||||||
|
@ -450,23 +418,15 @@ namespace sol {
|
||||||
return pointer;
|
return pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::size_t initial_size = aligned_space_for<T>(0x0);
|
constexpr std::size_t initial_size = aligned_space_for<T>();
|
||||||
constexpr std::size_t misaligned_size = aligned_space_for<T>(0x1);
|
|
||||||
|
|
||||||
std::size_t allocated_size = initial_size;
|
std::size_t allocated_size = initial_size;
|
||||||
void* unadjusted = alloc_newuserdata(L, allocated_size);
|
void* unadjusted = alloc_newuserdata(L, allocated_size);
|
||||||
void* adjusted = align(std::alignment_of_v<T>, unadjusted, allocated_size);
|
void* adjusted = align(std::alignment_of_v<T>, unadjusted, allocated_size);
|
||||||
if (adjusted == nullptr) {
|
|
||||||
lua_pop(L, 1);
|
|
||||||
// try again, add extra space for alignment padding
|
|
||||||
allocated_size = misaligned_size;
|
|
||||||
unadjusted = alloc_newuserdata(L, allocated_size);
|
|
||||||
adjusted = align(std::alignment_of_v<T>, unadjusted, allocated_size);
|
|
||||||
if (adjusted == nullptr) {
|
if (adjusted == nullptr) {
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T>().data());
|
luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T>().data());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return static_cast<T*>(adjusted);
|
return static_cast<T*>(adjusted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,10 @@ inline namespace sol2_regression_test_XXXX {
|
||||||
struct Test {
|
struct Test {
|
||||||
std::uint64_t dummy;
|
std::uint64_t dummy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct alignas(1024) Test2 {
|
||||||
|
char dummy[1024];
|
||||||
|
};
|
||||||
} // namespace sol2_regression_test_XXXX
|
} // namespace sol2_regression_test_XXXX
|
||||||
|
|
||||||
unsigned int regression_XXXX() {
|
unsigned int regression_XXXX() {
|
||||||
|
@ -20,6 +24,10 @@ unsigned int regression_XXXX() {
|
||||||
/// Note: may not panic depending on alignment of local variable `alignment_shim` in sol::detail::aligned_space_for
|
/// Note: may not panic depending on alignment of local variable `alignment_shim` in sol::detail::aligned_space_for
|
||||||
lua["test"] = Test {};
|
lua["test"] = Test {};
|
||||||
|
|
||||||
|
// Test also unique and over-aligned userdata
|
||||||
|
lua["test"] = std::make_unique<Test>();
|
||||||
|
lua["test"] = Test2 {};
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user