diff --git a/src/ziglua-5.1/lib.zig b/src/ziglua-5.1/lib.zig index a67576e..b6f306e 100644 --- a/src/ziglua-5.1/lib.zig +++ b/src/ziglua-5.1/lib.zig @@ -627,6 +627,7 @@ pub const Lua = struct { } /// This function allocates a new userdata of the given type. + /// Returns a pointer to the Lua-owned data /// See https://www.lua.org/manual/5.1/manual.html#lua_newuserdata pub fn newUserdata(lua: *Lua, comptime T: type) *T { // safe to .? because this function throws a Lua error on out of memory @@ -635,6 +636,15 @@ pub const Lua = struct { return opaqueCast(T, ptr); } + /// This function creates and pushes a slice of full userdata onto the stack. + /// Returns a slice to the Lua-owned data. + /// See https://www.lua.org/manual/5.1/manual.html#lua_newuserdata + pub fn newUserdataSlice(lua: *Lua, comptime T: type, size: usize) []T { + // safe to .? because this function throws a Lua error on out of memory + const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } + /// Pops a key from the stack, and pushes a key-value pair from the table at the given index. /// See https://www.lua.org/manual/5.1/manual.html#lua_next pub fn next(lua: *Lua, index: i32) bool { @@ -933,14 +943,27 @@ pub const Lua = struct { return error.Fail; } - /// Returns a pointer of the given type to the userdata at the given index. - /// Works for both full and light userdata. Otherwise returns an error. + /// Returns a Lua-owned userdata pointer of the given type at the given index. + /// Works for both light and full userdata. + /// Returns an error if the value is not a userdata. /// See https://www.lua.org/manual/5.1/manual.html#lua_touserdata pub fn toUserdata(lua: *Lua, comptime T: type, index: i32) !*T { if (c.lua_touserdata(lua.state, index)) |ptr| return opaqueCast(T, ptr); return error.Fail; } + /// Returns a Lua-owned userdata slice of the given type at the given index. + /// Returns an error if the value is not a userdata. + /// See https://www.lua.org/manual/5.1/manual.html#lua_touserdata + pub fn toUserdataSlice(lua: *Lua, comptime T: type, index: i32) ![]T { + if (c.lua_touserdata(lua.state, index)) |ptr| { + const size = lua.objectLen(index) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } + return error.Fail; + } + + /// Returns the `LuaType` of the value at the given index /// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf` /// See https://www.lua.org/manual/5.1/manual.html#lua_type @@ -1197,14 +1220,25 @@ pub const Lua = struct { c.luaL_checktype(lua.state, arg, @enumToInt(t)); } - /// Checks whether the function argument `arg` is a userdata of the type `type_name` + /// Checks whether the function argument `arg` is a userdata of the type `name` /// Returns the userdata's memory-block address /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkudata - pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32) *T { + pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) *T { // the returned pointer will not be null - return opaqueCast(T, c.luaL_checkudata(lua.state, arg, @typeName(T)).?); + return opaqueCast(T, c.luaL_checkudata(lua.state, arg, name.ptr).?); } + /// Checks whether the function argument `arg` is a userdata of the type `name` + /// Returns a Lua-owned userdata slice + /// See https://www.lua.org/manual/5.1/manual.html#luaL_checkudata + pub fn checkUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) []T { + // the returned pointer will not be null + const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; + const size = lua.objectLen(arg) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } + + /// Loads and runs the given file /// See https://www.lua.org/manual/5.1/manual.html#luaL_dofile pub fn doFile(lua: *Lua, file_name: [:0]const u8) !void { diff --git a/src/ziglua-5.1/tests.zig b/src/ziglua-5.1/tests.zig index 1938045..a3bcb8a 100644 --- a/src/ziglua-5.1/tests.zig +++ b/src/ziglua-5.1/tests.zig @@ -1159,11 +1159,11 @@ test "userdata" { defer lua.deinit(); const Type = struct { a: i32, b: f32 }; - try lua.newMetatable(@typeName(Type)); + try lua.newMetatable("Type"); const checkUdata = ziglua.wrap(struct { fn inner(l: *Lua) i32 { - const ptr = l.checkUserdata(Type, 1); + const ptr = l.checkUserdata(Type, 1, "Type"); if (ptr.a != 1234) { l.pushBytes("error!"); l.raiseError(); @@ -1180,7 +1180,7 @@ test "userdata" { { var t = lua.newUserdata(Type); - lua.getField(ziglua.registry_index, @typeName(Type)); + lua.getField(ziglua.registry_index, "Type"); lua.setMetatable(-2); t.a = 1234; t.b = 3.14; @@ -1191,6 +1191,39 @@ test "userdata" { } } +test "userdata slices" { + var lua = try Lua.init(testing.allocator); + defer lua.deinit(); + + try lua.newMetatable("FixedArray"); + + // create an array of 10 + const slice = lua.newUserdataSlice(Integer, 10); + lua.getField(ziglua.registry_index, "FixedArray"); + lua.setMetatable(-2); + + for (slice, 1..) |*item, index| { + item.* = @intCast(Integer, index); + } + + const udataFn = struct { + fn inner(l: *Lua) i32 { + _ = l.checkUserdataSlice(Integer, 1, "FixedArray"); + const arr = l.toUserdataSlice(Integer, 1) catch unreachable; + for (arr, 1..) |item, index| { + if (item != index) l.raiseErrorStr("something broke!", .{}); + } + + return 0; + } + }.inner; + + lua.pushFunction(ziglua.wrap(udataFn)); + lua.pushValue(2); + + try lua.protectedCall(1, 0, 0); +} + test "refs" { // tests for functions that aren't tested or will not be tested in ziglua // but ensures that the signatures are at least type checked diff --git a/src/ziglua-5.2/lib.zig b/src/ziglua-5.2/lib.zig index b8860d8..dc43f75 100644 --- a/src/ziglua-5.2/lib.zig +++ b/src/ziglua-5.2/lib.zig @@ -711,6 +711,7 @@ pub const Lua = struct { } /// This function allocates a new userdata of the given type + /// Returns a pointer to the Lua-owned data /// See https://www.lua.org/manual/5.2/manual.html#lua_newuserdata pub fn newUserdata(lua: *Lua, comptime T: type) *T { // safe to .? because this function throws a Lua error on out of memory @@ -719,6 +720,15 @@ pub const Lua = struct { return opaqueCast(T, ptr); } + /// This function creates and pushes a slice of full userdata onto the stack. + /// Returns a slice to the Lua-owned data. + /// See https://www.lua.org/manual/5.2/manual.html#lua_newuserdata + pub fn newUserdataSlice(lua: *Lua, comptime T: type, size: usize) []T { + // safe to .? because this function throws a Lua error on out of memory + const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } + /// Pops a key from the stack, and pushes a key-value pair from the table at the given index /// See https://www.lua.org/manual/5.2/manual.html#lua_next pub fn next(lua: *Lua, index: i32) bool { @@ -1084,14 +1094,26 @@ pub const Lua = struct { return result; } - /// Returns a pointer of the given type to the userdata at the given index. - /// Works for both full and light userdata. Otherwise returns an error. + /// Returns a Lua-owned userdata pointer of the given type at the given index. + /// Works for both light and full userdata. + /// Returns an error if the value is not a userdata. /// See https://www.lua.org/manual/5.2/manual.html#lua_touserdata pub fn toUserdata(lua: *Lua, comptime T: type, index: i32) !*T { if (c.lua_touserdata(lua.state, index)) |ptr| return opaqueCast(T, ptr); return error.Fail; } + /// Returns a Lua-owned userdata slice of the given type at the given index. + /// Returns an error if the value is not a userdata. + /// See https://www.lua.org/manual/5.2/manual.html#lua_touserdata + pub fn toUserdataSlice(lua: *Lua, comptime T: type, index: i32) ![]T { + if (c.lua_touserdata(lua.state, index)) |ptr| { + const size = lua.rawLen(index) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } + return error.Fail; + } + /// Returns the `LuaType` of the value at the given index /// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf` /// See https://www.lua.org/manual/5.2/manual.html#lua_type @@ -1389,12 +1411,22 @@ pub const Lua = struct { c.luaL_checktype(lua.state, arg, @enumToInt(t)); } - /// Checks whether the function argument `arg` is a userdata of the type `type_name` + /// Checks whether the function argument `arg` is a userdata of the type `name` /// Returns the userdata's memory-block address /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkudata - pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32) *T { + pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) *T { // the returned pointer will not be null - return opaqueCast(T, c.luaL_checkudata(lua.state, arg, @typeName(T)).?); + return opaqueCast(T, c.luaL_checkudata(lua.state, arg, name.ptr).?); + } + + /// Checks whether the function argument `arg` is a userdata of the type `name` + /// Returns a Lua-owned userdata slice + /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkudata + pub fn checkUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) []T { + // the returned pointer will not be null + const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; + const size = lua.rawLen(arg) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; } /// Checks whether the function argument arg is a number and returns this number cast to an unsigned @@ -1652,12 +1684,21 @@ pub const Lua = struct { /// This function works like `Lua.checkUserdata()` except it returns a Zig error instead of raising a Lua error on fail /// See https://www.lua.org/manual/5.2/manual.html#luaL_testudata - pub fn testUserdata(lua: *Lua, comptime T: type, arg: i32) !*T { - if (c.luaL_testudata(lua.state, arg, @typeName(T))) |ptr| { + pub fn testUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) !*T { + if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { return opaqueCast(T, ptr); } else return error.Fail; } + /// This function works like `Lua.checkUserdataSlice()` except it returns a Zig error instead of raising a Lua error on fail + /// See https://www.lua.org/manual/5.2/manual.html#luaL_checkudata + pub fn testUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) ![]T { + if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { + const size = lua.rawLen(arg) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } else return error.Fail; + } + /// Converts any Lua value at the given index into a string in a reasonable format /// See https://www.lua.org/manual/5.2/manual.html#luaL_tolstring pub fn toBytesFmt(lua: *Lua, index: i32) [:0]const u8 { diff --git a/src/ziglua-5.2/tests.zig b/src/ziglua-5.2/tests.zig index ff140c9..65ef359 100644 --- a/src/ziglua-5.2/tests.zig +++ b/src/ziglua-5.2/tests.zig @@ -1339,11 +1339,11 @@ test "userdata" { defer lua.deinit(); const Type = struct { a: i32, b: f32 }; - try lua.newMetatable(@typeName(Type)); + try lua.newMetatable("Type"); const checkUdata = ziglua.wrap(struct { fn inner(l: *Lua) i32 { - const ptr = l.checkUserdata(Type, 1); + const ptr = l.checkUserdata(Type, 1, "Type"); if (ptr.a != 1234) { _ = l.pushBytes("error!"); l.raiseError(); @@ -1360,7 +1360,7 @@ test "userdata" { { var t = lua.newUserdata(Type); - lua.setMetatableRegistry(@typeName(Type)); + lua.setMetatableRegistry("Type"); t.a = 1234; t.b = 3.14; @@ -1371,7 +1371,7 @@ test "userdata" { const testUdata = ziglua.wrap(struct { fn inner(l: *Lua) i32 { - const ptr = l.testUserdata(Type, 1) catch { + const ptr = l.testUserdata(Type, 1, "Type") catch { _ = l.pushBytes("error!"); l.raiseError(); }; @@ -1391,7 +1391,7 @@ test "userdata" { { var t = lua.newUserdata(Type); - lua.setMetatableRegistry(@typeName(Type)); + lua.setMetatableRegistry("Type"); t.a = 1234; t.b = 3.14; @@ -1401,6 +1401,38 @@ test "userdata" { } } +test "userdata slices" { + var lua = try Lua.init(testing.allocator); + defer lua.deinit(); + + try lua.newMetatable("FixedArray"); + + // create an array of 10 + const slice = lua.newUserdataSlice(Integer, 10); + lua.setMetatableRegistry("FixedArray"); + for (slice, 1..) |*item, index| { + item.* = @intCast(Integer, index); + } + + const udataFn = struct { + fn inner(l: *Lua) i32 { + _ = l.checkUserdataSlice(Integer, 1, "FixedArray"); + _ = l.testUserdataSlice(Integer, 1, "FixedArray") catch unreachable; + const arr = l.toUserdataSlice(Integer, 1) catch unreachable; + for (arr, 1..) |item, index| { + if (item != index) l.raiseErrorStr("something broke!", .{}); + } + + return 0; + } + }.inner; + + lua.pushFunction(ziglua.wrap(udataFn)); + lua.pushValue(2); + + try lua.protectedCall(1, 0, 0); +} + test "refs" { // tests for functions that aren't tested or will not be tested in ziglua // but ensures that the signatures are at least type checked diff --git a/src/ziglua-5.3/lib.zig b/src/ziglua-5.3/lib.zig index ffac350..8c764ba 100644 --- a/src/ziglua-5.3/lib.zig +++ b/src/ziglua-5.3/lib.zig @@ -735,9 +735,8 @@ pub const Lua = struct { return .{ .state = state }; } - /// This function allocates a new block of memory with the given size, - /// pushes onto the stack a new full userdata with the block address, - /// and returns this address. The host program can freely use this memory + /// This function allocates a new userdata of the given type. + /// Returns a pointer to the Lua-owned data /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata pub fn newUserdata(lua: *Lua, comptime T: type) *T { // safe to .? because this function throws a Lua error on out of memory @@ -746,6 +745,15 @@ pub const Lua = struct { return opaqueCast(T, ptr); } + /// This function creates and pushes a slice of full userdata onto the stack. + /// Returns a slice to the Lua-owned data. + /// See https://www.lua.org/manual/5.3/manual.html#lua_newuserdata + pub fn newUserdataSlice(lua: *Lua, comptime T: type, size: usize) []T { + // safe to .? because this function throws a Lua error on out of memory + const ptr = c.lua_newuserdata(lua.state, @sizeOf(T) * size).?; + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } + /// Pops a key from the stack, and pushes a key-value pair from the table at the given index /// See https://www.lua.org/manual/5.3/manual.html#lua_next pub fn next(lua: *Lua, index: i32) bool { @@ -1135,14 +1143,26 @@ pub const Lua = struct { return error.Fail; } - /// Returns a pointer of the given type to the userdata at the given index. - /// Works for both full and light userdata. Otherwise returns an error. + /// Returns a Lua-owned userdata pointer of the given type at the given index. + /// Works for both light and full userdata. + /// Returns an error if the value is not a userdata. /// See https://www.lua.org/manual/5.3/manual.html#lua_touserdata pub fn toUserdata(lua: *Lua, comptime T: type, index: i32) !*T { if (c.lua_touserdata(lua.state, index)) |ptr| return opaqueCast(T, ptr); return error.Fail; } + /// Returns a Lua-owned userdata slice of the given type at the given index. + /// Returns an error if the value is not a userdata. + /// See https://www.lua.org/manual/5.3/manual.html#lua_touserdata + pub fn toUserdataSlice(lua: *Lua, comptime T: type, index: i32) ![]T { + if (c.lua_touserdata(lua.state, index)) |ptr| { + const size = lua.rawLen(index) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } + return error.Fail; + } + /// Returns the `LuaType` of the value at the given index /// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf` /// See https://www.lua.org/manual/5.3/manual.html#lua_typeof @@ -1435,12 +1455,22 @@ pub const Lua = struct { c.luaL_checktype(lua.state, arg, @enumToInt(t)); } - /// Checks whether the function argument `arg` is a userdata of the type `type_name` - /// Returns the userdata's memory-block address + /// Checks whether the function argument `arg` is a userdata of the type `name` + /// Returns a pointer to the userdata /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkudata - pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32) *T { + pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) *T { // the returned pointer will not be null - return opaqueCast(T, c.luaL_checkudata(lua.state, arg, @typeName(T)).?); + return opaqueCast(T, c.luaL_checkudata(lua.state, arg, name.ptr).?); + } + + /// Checks whether the function argument `arg` is a userdata of the type `name` + /// Returns a Lua-owned userdata slice + /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkudata + pub fn checkUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) []T { + // the returned pointer will not be null + const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; + const size = lua.rawLen(arg) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; } /// Checks whether the code making the call and the Lua library being called are using @@ -1678,12 +1708,21 @@ pub const Lua = struct { /// This function works like `Lua.checkUserdata()` except it returns a Zig error instead of raising a Lua error on fail /// See https://www.lua.org/manual/5.3/manual.html#luaL_testudata - pub fn testUserdata(lua: *Lua, comptime T: type, arg: i32) !*T { - if (c.luaL_testudata(lua.state, arg, @typeName(T))) |ptr| { + pub fn testUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) !*T { + if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { return opaqueCast(T, ptr); } else return error.Fail; } + /// This function works like `Lua.checkUserdataSlice()` except it returns a Zig error instead of raising a Lua error on fail + /// See https://www.lua.org/manual/5.3/manual.html#luaL_checkudata + pub fn testUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) ![]T { + if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { + const size = lua.rawLen(arg) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } else return error.Fail; + } + /// Converts any Lua value at the given index into a string in a reasonable format /// See https://www.lua.org/manual/5.3/manual.html#luaL_tolstring pub fn toBytesFmt(lua: *Lua, index: i32) [:0]const u8 { diff --git a/src/ziglua-5.3/tests.zig b/src/ziglua-5.3/tests.zig index 26c9948..307d8dd 100644 --- a/src/ziglua-5.3/tests.zig +++ b/src/ziglua-5.3/tests.zig @@ -1364,16 +1364,16 @@ test "userdata" { defer lua.deinit(); const Type = struct { a: i32, b: f32 }; - try lua.newMetatable(@typeName(Type)); + try lua.newMetatable("Type"); var t = lua.newUserdata(Type); - lua.setMetatableRegistry(@typeName(Type)); + lua.setMetatableRegistry("Type"); t.a = 1234; t.b = 3.14; const checkUdata = ziglua.wrap(struct { fn inner(l: *Lua) i32 { - const ptr = l.checkUserdata(Type, 1); + const ptr = l.checkUserdata(Type, 1, "Type"); if (ptr.a != 1234) { _ = l.pushBytes("error!"); l.raiseError(); @@ -1395,7 +1395,7 @@ test "userdata" { const testUdata = ziglua.wrap(struct { fn inner(l: *Lua) i32 { - const ptr = l.testUserdata(Type, 1) catch { + const ptr = l.testUserdata(Type, 1, "Type") catch { _ = l.pushBytes("error!"); l.raiseError(); }; @@ -1419,6 +1419,38 @@ test "userdata" { try lua.protectedCall(1, 0, 0); } +test "userdata slices" { + var lua = try Lua.init(testing.allocator); + defer lua.deinit(); + + try lua.newMetatable("FixedArray"); + + // create an array of 10 + const slice = lua.newUserdataSlice(Integer, 10); + lua.setMetatableRegistry("FixedArray"); + for (slice, 1..) |*item, index| { + item.* = @intCast(Integer, index); + } + + const udataFn = struct { + fn inner(l: *Lua) i32 { + _ = l.checkUserdataSlice(Integer, 1, "FixedArray"); + _ = l.testUserdataSlice(Integer, 1, "FixedArray") catch unreachable; + const arr = l.toUserdataSlice(Integer, 1) catch unreachable; + for (arr, 1..) |item, index| { + if (item != index) l.raiseErrorStr("something broke!", .{}); + } + + return 0; + } + }.inner; + + lua.pushFunction(ziglua.wrap(udataFn)); + lua.rotate(-2, 1); + + try lua.protectedCall(1, 0, 0); +} + test "refs" { // tests for functions that aren't tested or will not be tested in ziglua // but ensures that the signatures are at least type checked diff --git a/src/ziglua-5.4/lib.zig b/src/ziglua-5.4/lib.zig index dc89ccd..036b7d2 100644 --- a/src/ziglua-5.4/lib.zig +++ b/src/ziglua-5.4/lib.zig @@ -747,17 +747,25 @@ pub const Lua = struct { return .{ .state = state }; } - /// This function creates and pushes a new full userdata onto the stack - /// with upvalues associated Lua values, plus an associated block of raw memory with size bytes - /// Returns the address of the block of memory + /// This function allocates a new userdata of the given type with user_values associated Lua values. + /// Returns a pointer to the Lua-owned data /// See https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv - pub fn newUserdata(lua: *Lua, comptime T: type, upvalues: i32) *T { + pub fn newUserdata(lua: *Lua, comptime T: type, user_values: i32) *T { // safe to .? because this function throws a Lua error on out of memory // so the returned pointer should never be null - const ptr = c.lua_newuserdatauv(lua.state, @sizeOf(T), upvalues).?; + const ptr = c.lua_newuserdatauv(lua.state, @sizeOf(T), user_values).?; return opaqueCast(T, ptr); } + /// This function creates and pushes a slice of full userdata onto the stack with user_values associated Lua values. + /// Returns a slice to the Lua-owned data. + /// See https://www.lua.org/manual/5.4/manual.html#lua_newuserdatauv + pub fn newUserdataSlice(lua: *Lua, comptime T: type, size: usize, user_values: i32) []T { + // safe to .? because this function throws a Lua error on out of memory + const ptr = c.lua_newuserdatauv(lua.state, @sizeOf(T) * size, user_values).?; + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } + /// Pops a key from the stack, and pushes a key-value pair from the table at the given index /// See https://www.lua.org/manual/5.4/manual.html#lua_next pub fn next(lua: *Lua, index: i32) bool { @@ -1164,14 +1172,26 @@ pub const Lua = struct { return error.Fail; } - /// Returns a pointer of the given type to the userdata at the given index. - /// Works for both full and light userdata. Otherwise returns an error. + /// Returns a Lua-owned userdata pointer of the given type at the given index. + /// Works for both light and full userdata. + /// Returns an error if the value is not a userdata. /// See https://www.lua.org/manual/5.4/manual.html#lua_touserdata pub fn toUserdata(lua: *Lua, comptime T: type, index: i32) !*T { if (c.lua_touserdata(lua.state, index)) |ptr| return opaqueCast(T, ptr); return error.Fail; } + /// Returns a Lua-owned userdata slice of the given type at the given index. + /// Returns an error if the value is not a userdata. + /// See https://www.lua.org/manual/5.4/manual.html#lua_touserdata + pub fn toUserdataSlice(lua: *Lua, comptime T: type, index: i32) ![]T { + if (c.lua_touserdata(lua.state, index)) |ptr| { + const size = lua.rawLen(index) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } + return error.Fail; + } + /// Returns the `LuaType` of the value at the given index /// Note that this is equivalent to lua_type but because type is a Zig primitive it is renamed to `typeOf` /// See https://www.lua.org/manual/5.4/manual.html#lua_type @@ -1480,12 +1500,22 @@ pub const Lua = struct { c.luaL_checktype(lua.state, arg, @enumToInt(t)); } - /// Checks whether the function argument `arg` is a userdata of the type `type_name` - /// Returns the userdata's memory-block address + /// Checks whether the function argument `arg` is a userdata of the type `name` + /// Returns a pointer to the userdata + /// See https://www.lua.org/manual/5.4/manual.html#lua_checkudata + pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) *T { + // the returned pointer will not be null + return opaqueCast(T, c.luaL_checkudata(lua.state, arg, name.ptr).?); + } + + /// Checks whether the function argument `arg` is a userdata of the type `name` + /// Returns a Lua-owned userdata slice /// See https://www.lua.org/manual/5.4/manual.html#lua_checkudata - pub fn checkUserdata(lua: *Lua, comptime T: type, arg: i32) *T { + pub fn checkUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) []T { // the returned pointer will not be null - return opaqueCast(T, c.luaL_checkudata(lua.state, arg, @typeName(T)).?); + const ptr = c.luaL_checkudata(lua.state, arg, name.ptr).?; + const size = lua.rawLen(arg) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; } /// Checks whether the code making the call and the Lua library being called are using @@ -1730,12 +1760,21 @@ pub const Lua = struct { /// This function works like `Lua.checkUserdata()` except it returns a Zig error instead of raising a Lua error on fail /// See https://www.lua.org/manual/5.4/manual.html#lua_testudata - pub fn testUserdata(lua: *Lua, comptime T: type, arg: i32) !*T { - if (c.luaL_testudata(lua.state, arg, @typeName(T))) |ptr| { + pub fn testUserdata(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) !*T { + if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { return opaqueCast(T, ptr); } else return error.Fail; } + /// This function works like `Lua.checkUserdataSlice()` except it returns a Zig error instead of raising a Lua error on fail + /// See https://www.lua.org/manual/5.4/manual.html#luaL_checkudata + pub fn testUserdataSlice(lua: *Lua, comptime T: type, arg: i32, name: [:0]const u8) ![]T { + if (c.luaL_testudata(lua.state, arg, name.ptr)) |ptr| { + const size = lua.rawLen(arg) / @sizeOf(T); + return @ptrCast([*]T, @alignCast(@alignOf([*]T), ptr))[0..size]; + } else return error.Fail; + } + /// Converts any Lua value at the given index into a string in a reasonable format /// See https://www.lua.org/manual/5.4/manual.html#lua_tolstring pub fn toBytesFmt(lua: *Lua, index: i32) [:0]const u8 { diff --git a/src/ziglua-5.4/tests.zig b/src/ziglua-5.4/tests.zig index 5a4ba4d..8dd6e91 100644 --- a/src/ziglua-5.4/tests.zig +++ b/src/ziglua-5.4/tests.zig @@ -1459,16 +1459,16 @@ test "userdata" { defer lua.deinit(); const Type = struct { a: i32, b: f32 }; - try lua.newMetatable(@typeName(Type)); + try lua.newMetatable("Type"); var t = lua.newUserdata(Type, 0); - lua.setMetatableRegistry(@typeName(Type)); + lua.setMetatableRegistry("Type"); t.a = 1234; t.b = 3.14; const checkUdata = ziglua.wrap(struct { fn inner(l: *Lua) i32 { - const ptr = l.checkUserdata(Type, 1); + const ptr = l.checkUserdata(Type, 1, "Type"); if (ptr.a != 1234) { _ = l.pushBytes("error!"); l.raiseError(); @@ -1490,7 +1490,7 @@ test "userdata" { const testUdata = ziglua.wrap(struct { fn inner(l: *Lua) i32 { - const ptr = l.testUserdata(Type, 1) catch { + const ptr = l.testUserdata(Type, 1, "Type") catch { _ = l.pushBytes("error!"); l.raiseError(); }; @@ -1514,6 +1514,38 @@ test "userdata" { try lua.protectedCall(1, 0, 0); } +test "userdata slices" { + var lua = try Lua.init(testing.allocator); + defer lua.deinit(); + + try lua.newMetatable("FixedArray"); + + // create an array of 10 + const slice = lua.newUserdataSlice(Integer, 10, 0); + lua.setMetatableRegistry("FixedArray"); + for (slice, 1..) |*item, index| { + item.* = @intCast(Integer, index); + } + + const udataFn = struct { + fn inner(l: *Lua) i32 { + _ = l.checkUserdataSlice(Integer, 1, "FixedArray"); + _ = l.testUserdataSlice(Integer, 1, "FixedArray") catch unreachable; + const arr = l.toUserdataSlice(Integer, 1) catch unreachable; + for (arr, 1..) |item, index| { + if (item != index) l.raiseErrorStr("something broke!", .{}); + } + + return 0; + } + }.inner; + + lua.pushFunction(ziglua.wrap(udataFn)); + lua.rotate(-2, 1); + + try lua.protectedCall(1, 0, 0); +} + test "refs" { // tests for functions that aren't tested or will not be tested in ziglua // but ensures that the signatures are at least type checked