Skip to content

Commit e34f94d

Browse files
committed
Manually align userdata types with high alignment.
1 parent ce603c1 commit e34f94d

File tree

1 file changed

+46
-6
lines changed

1 file changed

+46
-6
lines changed

src/util.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,39 @@ unsafe fn newuserdatauv(state: *mut ffi::lua_State, size: usize, nuvalues: c_int
239239
ffi::lua_newuserdata(state, size)
240240
}
241241

242+
// Return the padded allocation size for a userdata type. Lua itself only guarantees alignment up
243+
// to the largest type in LUAI_MAXALIGN (which includes double/f64), so we need to pad the
244+
// allocation and manually align for types with higher alignment requirements.
245+
fn get_userdata_padded_size<T>() -> usize {
246+
let size = mem::size_of::<T>();
247+
let align = mem::align_of::<T>();
248+
249+
if align > mem::align_of::<f64>() {
250+
// Enough space to guarantee being able to align the type.
251+
size + align - mem::align_of::<f64>()
252+
} else {
253+
size
254+
}
255+
}
256+
257+
// Convert a *mut u8 pointer returned from lua_newuserdatauv to an aligned
258+
// pointer to T. This assumes that extra space was allocated according to
259+
// get_userdata_padded_size().
260+
// Returns null if the input is null.
261+
fn align_userdata_ptr<T>(raw_ptr: *mut u8) -> *mut T {
262+
let align = mem::align_of::<T>();
263+
if raw_ptr.is_null() {
264+
return ptr::null_mut();
265+
}
266+
267+
if align > mem::align_of::<f64>() {
268+
let offset = raw_ptr.align_offset(align);
269+
raw_ptr.wrapping_add(offset) as *mut T
270+
} else {
271+
raw_ptr as *mut T
272+
}
273+
}
274+
242275
#[cfg(rlua_lua54)]
243276
// Internally uses 4 stack spaces, does not call checkstack
244277
pub unsafe fn push_userdata_uv<T>(
@@ -251,7 +284,10 @@ pub unsafe fn push_userdata_uv<T>(
251284
"userdata user values cannot be below zero"
252285
);
253286
let ud = protect_lua_closure(state, 0, 1, move |state| {
254-
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), uvalues_count) as *mut T
287+
let alloc_size = get_userdata_padded_size::<T>();
288+
let raw_ptr = ffi::lua_newuserdatauv(state, alloc_size, uvalues_count) as *mut u8;
289+
290+
align_userdata_ptr(raw_ptr)
255291
})?;
256292
ptr::write(ud, t);
257293
Ok(())
@@ -273,15 +309,19 @@ pub unsafe fn push_userdata_uv<T>(
273309
"This version of Lua only supports one user value."
274310
);
275311
let ud = protect_lua_closure(state, 0, 1, move |state| {
276-
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
312+
let alloc_size = get_userdata_padded_size::<T>();
313+
let raw_ptr = ffi::lua_newuserdata(state, alloc_size) as *mut u8;
314+
315+
align_userdata_ptr(raw_ptr)
277316
})?;
278317
ptr::write(ud, t);
279318
Ok(())
280319
}
320+
281321
pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
282-
let ud = ffi::lua_touserdata(state, index) as *mut T;
283-
rlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
284-
ud
322+
let raw_ud = ffi::lua_touserdata(state, index) as *mut u8;
323+
rlua_debug_assert!(!raw_ud.is_null(), "userdata pointer is null");
324+
align_userdata_ptr(raw_ud)
285325
}
286326

287327
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
@@ -295,7 +335,7 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
295335
// after the first call to __gc.
296336
get_destructed_userdata_metatable(state);
297337
ffi::lua_setmetatable(state, -2);
298-
let ud = ffi::lua_touserdata(state, -1) as *mut T;
338+
let ud = align_userdata_ptr::<T>(ffi::lua_touserdata(state, -1) as *mut u8);
299339
rlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
300340
ffi::lua_pop(state, 1);
301341
ptr::read(ud)

0 commit comments

Comments
 (0)