@@ -239,6 +239,39 @@ unsafe fn newuserdatauv(state: *mut ffi::lua_State, size: usize, nuvalues: c_int
239
239
ffi:: lua_newuserdata ( state, size)
240
240
}
241
241
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
+
242
275
#[ cfg( rlua_lua54) ]
243
276
// Internally uses 4 stack spaces, does not call checkstack
244
277
pub unsafe fn push_userdata_uv < T > (
@@ -251,7 +284,10 @@ pub unsafe fn push_userdata_uv<T>(
251
284
"userdata user values cannot be below zero"
252
285
) ;
253
286
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)
255
291
} ) ?;
256
292
ptr:: write ( ud, t) ;
257
293
Ok ( ( ) )
@@ -273,15 +309,19 @@ pub unsafe fn push_userdata_uv<T>(
273
309
"This version of Lua only supports one user value."
274
310
) ;
275
311
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)
277
316
} ) ?;
278
317
ptr:: write ( ud, t) ;
279
318
Ok ( ( ) )
280
319
}
320
+
281
321
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 )
285
325
}
286
326
287
327
// 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 {
295
335
// after the first call to __gc.
296
336
get_destructed_userdata_metatable ( state) ;
297
337
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 ) ;
299
339
rlua_debug_assert ! ( !ud. is_null( ) , "userdata pointer is null" ) ;
300
340
ffi:: lua_pop ( state, 1 ) ;
301
341
ptr:: read ( ud)
0 commit comments