@@ -571,6 +571,21 @@ pub const CWarnFn = switch (lang) {
571
571
/// The type of the writer function used by `Lua.dump()`
572
572
pub const CWriterFn = * const fn (state : ? * LuaState , buf : ? * const anyopaque , size : usize , data : ? * anyopaque ) callconv (.C ) c_int ;
573
573
574
+ /// For bundling a parsed value with an arena allocator
575
+ /// Copied from std.json.Parsed
576
+ pub fn Parsed (comptime T : type ) type {
577
+ return struct {
578
+ arena : * std.heap.ArenaAllocator ,
579
+ value : T ,
580
+
581
+ pub fn deinit (self : @This ()) void {
582
+ const allocator = self .arena .child_allocator ;
583
+ self .arena .deinit ();
584
+ allocator .destroy (self .arena );
585
+ }
586
+ };
587
+ }
588
+
574
589
/// A Zig wrapper around the Lua C API
575
590
/// Represents a Lua state or thread and contains the entire state of the Lua interpreter
576
591
pub const Lua = struct {
@@ -1658,8 +1673,7 @@ pub const Lua = struct {
1658
1673
}
1659
1674
}
1660
1675
1661
- /// Similar to `Lua.setTable()` but does a raw asskdjfal;sdkfjals;dkfj;dk:q
1662
- /// gnment (without metamethods)
1676
+ /// Similar to `Lua.setTable()` but does a raw assignment (without metamethods)
1663
1677
/// See https://www.lua.org/manual/5.4/manual.html#lua_rawset
1664
1678
pub fn rawSetTable (lua : * Lua , index : i32 ) void {
1665
1679
c .lua_rawset (lua .state , index );
@@ -3145,7 +3159,42 @@ pub const Lua = struct {
3145
3159
3146
3160
/// Converts the specified index of the lua stack to the specified
3147
3161
/// type if possible and returns it
3148
- pub fn toAny (lua : * Lua , comptime T : type , index : i32 ) ! T {
3162
+ /// Allocates memory if necessary
3163
+ pub fn toAnyAlloc (lua : * Lua , comptime T : type , index : i32 ) ! Parsed (T ) {
3164
+ var parsed = Parsed (T ){
3165
+ .arena = try lua .allocator ().create (std .heap .ArenaAllocator ),
3166
+ .value = undefined ,
3167
+ };
3168
+ errdefer lua .allocator ().destroy (parsed .arena );
3169
+ parsed .arena .* = std .heap .ArenaAllocator .init (lua .allocator ());
3170
+ errdefer parsed .arena .deinit ();
3171
+
3172
+ parsed .value = try lua .toAnyInternal (T , parsed .arena .allocator (), true , index );
3173
+
3174
+ return parsed ;
3175
+ }
3176
+
3177
+ /// Converts the specified index of the lua stack to the specified
3178
+ /// type if possible and returns it
3179
+ /// Does not allocate any memory, if memory allocation is needed (such as for parsing slices)
3180
+ /// use toAnyAlloc
3181
+ pub inline fn toAny (lua : * Lua , comptime T : type , index : i32 ) ! T {
3182
+ return lua .toAnyInternal (T , null , false , index );
3183
+ }
3184
+
3185
+ /// Converts the specified index of the lua stack to the specified
3186
+ /// type if possible and returns it
3187
+ /// optional allocator
3188
+ fn toAnyInternal (lua : * Lua , comptime T : type , a : ? std.mem.Allocator , comptime allow_alloc : bool , index : i32 ) ! T {
3189
+ const stack_size_on_entry = lua .getTop ();
3190
+ defer {
3191
+ if (lua .getTop () != stack_size_on_entry ) {
3192
+ std .debug .print ("Type that filed to parse was: {any}\n " , .{T });
3193
+ std .debug .print ("Expected stack size: {}, Actual Stack Size: {}\n\n " , .{ stack_size_on_entry , lua .getTop () });
3194
+ @panic ("internal parsing error" );
3195
+ }
3196
+ }
3197
+
3149
3198
switch (@typeInfo (T )) {
3150
3199
.Int = > {
3151
3200
switch (comptime lang ) {
@@ -3183,7 +3232,10 @@ pub const Lua = struct {
3183
3232
}
3184
3233
} else switch (info .size ) {
3185
3234
.Slice , .Many = > {
3186
- return try lua .toSlice (info .child , index );
3235
+ if (! allow_alloc ) {
3236
+ @compileError ("toAny cannot allocate memory, try using toAnyAlloc" );
3237
+ }
3238
+ return try lua .toSlice (info .child , a .? , index );
3187
3239
},
3188
3240
else = > {
3189
3241
return try lua .toUserdata (info .child , index );
@@ -3194,7 +3246,7 @@ pub const Lua = struct {
3194
3246
return lua .toBoolean (index );
3195
3247
},
3196
3248
.Enum = > | info | {
3197
- const string = try lua .toAny ([]const u8 , index );
3249
+ const string = try lua .toAnyInternal ([]const u8 , a , allow_alloc , index );
3198
3250
inline for (info .fields ) | enum_member | {
3199
3251
if (std .mem .eql (u8 , string , enum_member .name )) {
3200
3252
return @field (T , enum_member .name );
@@ -3203,14 +3255,13 @@ pub const Lua = struct {
3203
3255
return error .InvalidEnumTagName ;
3204
3256
},
3205
3257
.Struct = > {
3206
- return try lua .toStruct (T , index );
3258
+ return try lua .toStruct (T , a , allow_alloc , index );
3207
3259
},
3208
3260
.Optional = > {
3209
3261
if (lua .isNil (index )) {
3210
- lua .pop (1 );
3211
3262
return null ;
3212
3263
} else {
3213
- return try lua .toAny (@typeInfo (T ).Optional .child , index );
3264
+ return try lua .toAnyInternal (@typeInfo (T ).Optional .child , a , allow_alloc , index );
3214
3265
}
3215
3266
},
3216
3267
else = > {
@@ -3220,27 +3271,31 @@ pub const Lua = struct {
3220
3271
}
3221
3272
3222
3273
/// Converts a lua array to a zig slice, memory is owned by the caller
3223
- fn toSlice (lua : * Lua , comptime ChildType : type , raw_index : i32 ) ! []ChildType {
3274
+ fn toSlice (lua : * Lua , comptime ChildType : type , a : std.mem.Allocator , raw_index : i32 ) ! []ChildType {
3224
3275
const index = lua .absIndex (raw_index );
3225
3276
3226
3277
if (! lua .isTable (index )) {
3227
3278
return error .ValueNotATable ;
3228
3279
}
3229
3280
3230
3281
const size = lua .rawLen (index );
3231
- var result = try lua . allocator () .alloc (ChildType , size );
3282
+ var result = try a .alloc (ChildType , size );
3232
3283
3233
3284
for (1.. size + 1 ) | i | {
3234
3285
_ = try lua .pushAny (i );
3235
3286
_ = lua .getTable (index );
3236
- result [i - 1 ] = try lua .toAny (ChildType , -1 );
3287
+ result [i - 1 ] = try lua .toAnyInternal (ChildType , a , true , -1 );
3288
+ lua .pop (1 );
3237
3289
}
3238
3290
3239
3291
return result ;
3240
3292
}
3241
3293
3242
3294
/// Converts value at given index to a zig struct if possible
3243
- fn toStruct (lua : * Lua , comptime T : type , raw_index : i32 ) ! T {
3295
+ fn toStruct (lua : * Lua , comptime T : type , a : ? std.mem.Allocator , comptime allow_alloc : bool , raw_index : i32 ) ! T {
3296
+ const stack_size_on_entry = lua .getTop ();
3297
+ defer std .debug .assert (lua .getTop () == stack_size_on_entry );
3298
+
3244
3299
const index = lua .absIndex (raw_index );
3245
3300
3246
3301
if (! lua .isTable (index )) {
@@ -3249,27 +3304,32 @@ pub const Lua = struct {
3249
3304
std .debug .assert (lua .typeOf (index ) == .table );
3250
3305
3251
3306
var result : T = undefined ;
3307
+
3252
3308
inline for (@typeInfo (T ).Struct .fields ) | field | {
3253
3309
const field_name = comptime field .name ++ "" ;
3254
3310
_ = lua .pushString (field_name );
3255
- std . debug . assert ( lua . typeOf ( index ) == .table );
3311
+
3256
3312
const lua_field_type = lua .getTable (index );
3257
3313
if (lua_field_type == .nil ) {
3258
3314
if (field .default_value ) | default_value | {
3259
3315
@field (result , field .name ) = @as (* const field .type , @ptrCast (@alignCast (default_value ))).* ;
3260
3316
} else {
3317
+ lua .pop (1 );
3261
3318
return error .LuaTableMissingValue ;
3262
3319
}
3263
3320
} else {
3264
- @field (result , field .name ) = try lua .toAny (field .type , -1 );
3321
+ const stack_size_before_call = lua .getTop ();
3322
+ @field (result , field .name ) = try lua .toAnyInternal (field .type , a , allow_alloc , -1 );
3323
+ std .debug .assert (stack_size_before_call == lua .getTop ());
3265
3324
}
3325
+ lua .pop (1 ); //pop the value off the stack
3266
3326
}
3267
3327
3268
3328
return result ;
3269
3329
}
3270
3330
3271
- ///automatically calls a lua function with the given arguments
3272
- pub fn autoCall (lua : * Lua , comptime ReturnType : type , func_name : [:0 ]const u8 , args : anytype ) ! ReturnType {
3331
+ /// Calls a function and pushes its return value to the top of the stack
3332
+ fn autoCallAndPush (lua : * Lua , comptime ReturnType : type , func_name : [:0 ]const u8 , args : anytype ) ! void {
3273
3333
if (try lua .getGlobal (func_name ) != LuaType .function ) return error .InvalidFunctionName ;
3274
3334
3275
3335
inline for (args ) | arg | {
@@ -3278,9 +3338,22 @@ pub const Lua = struct {
3278
3338
3279
3339
const num_results = if (ReturnType == void ) 0 else 1 ;
3280
3340
try lua .protectedCall (args .len , num_results , 0 );
3281
- defer lua . setTop ( 0 );
3341
+ }
3282
3342
3283
- return lua .toAny (ReturnType , -1 );
3343
+ ///automatically calls a lua function with the given arguments
3344
+ pub fn autoCall (lua : * Lua , comptime ReturnType : type , func_name : [:0 ]const u8 , args : anytype ) ! ReturnType {
3345
+ try lua .autoCallAndPush (ReturnType , func_name , args );
3346
+ const result = try lua .toAny (ReturnType , -1 );
3347
+ lua .setTop (0 );
3348
+ return result ;
3349
+ }
3350
+
3351
+ ///automatically calls a lua function with the given arguments
3352
+ pub fn autoCallAlloc (lua : * Lua , comptime ReturnType : type , func_name : [:0 ]const u8 , args : anytype ) ! Parsed (ReturnType ) {
3353
+ try lua .autoCallAndPush (ReturnType , func_name , args );
3354
+ const result = try lua .toAnyAlloc (ReturnType , -1 );
3355
+ lua .setTop (0 );
3356
+ return result ;
3284
3357
}
3285
3358
3286
3359
//automatically generates a wrapper function
@@ -3296,9 +3369,23 @@ pub const Lua = struct {
3296
3369
var parameters : std .meta .ArgsTuple (@TypeOf (function )) = undefined ;
3297
3370
3298
3371
inline for (info .Fn .params , 0.. ) | param , i | {
3299
- parameters [i ] = lua .toAny (param .type .? , (i + 1 )) catch | err | {
3300
- lua .raiseErrorStr (@errorName (err ), .{});
3301
- };
3372
+ const param_info = @typeInfo (param .type .? );
3373
+ //only use the overhead of creating the arena allocator if needed
3374
+ if (comptime param_info == .Pointer and param_info .Pointer .size != .One ) {
3375
+ const parsed = lua .toAnyAlloc (param .type .? , (i + 1 )) catch | err | {
3376
+ lua .raiseErrorStr (@errorName (err ), .{});
3377
+ };
3378
+
3379
+ defer parsed .deinit ();
3380
+
3381
+ parameters [i ] = parsed .value ;
3382
+ } else {
3383
+ const parsed = lua .toAny (param .type .? , (i + 1 )) catch | err | {
3384
+ lua .raiseErrorStr (@errorName (err ), .{});
3385
+ };
3386
+
3387
+ parameters [i ] = parsed ;
3388
+ }
3302
3389
}
3303
3390
3304
3391
if (@typeInfo (info .Fn .return_type .? ) == .ErrorUnion ) {
@@ -3332,6 +3419,13 @@ pub const Lua = struct {
3332
3419
return try lua .toAny (ReturnType , -1 );
3333
3420
}
3334
3421
3422
+ /// get any lua global
3423
+ /// can allocate memory
3424
+ pub fn getAlloc (lua : * Lua , comptime ReturnType : type , name : [:0 ]const u8 ) ! Parsed (ReturnType ) {
3425
+ _ = try lua .getGlobal (name );
3426
+ return try lua .toAnyAlloc (ReturnType , -1 );
3427
+ }
3428
+
3335
3429
///set any lua global
3336
3430
pub fn set (lua : * Lua , name : [:0 ]const u8 , value : anytype ) ! void {
3337
3431
try lua .pushAny (value );
0 commit comments