Support 52-bit integers for Luau

Simply to float conversion (it actually never fails or goes out of range)
This commit is contained in:
Alex Orlenko
2025-03-13 16:18:44 +00:00
parent 2543414726
commit ef8b8e11ec
7 changed files with 77 additions and 44 deletions
+25
View File
@@ -125,6 +125,11 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int {
0
}
#[inline(always)]
pub unsafe fn lua_pushinteger(L: *mut lua_State, i: lua_Integer) {
lua_pushnumber(L, i as lua_Number);
}
#[inline(always)]
pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx(L, i, ptr::null_mut())
@@ -176,6 +181,7 @@ pub unsafe fn lua_geti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) -> c_i
#[inline(always)]
pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int {
let n = n.try_into().expect("cannot convert index from lua_Integer");
lua_rawgeti_(L, idx, n)
}
@@ -211,6 +217,7 @@ pub unsafe fn lua_seti(L: *mut lua_State, mut idx: c_int, n: lua_Integer) {
#[inline(always)]
pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) {
let n = n.try_into().expect("cannot convert index from lua_Integer");
lua_rawseti_(L, idx, n)
}
@@ -300,6 +307,24 @@ pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char)
}
}
#[inline(always)]
pub unsafe fn luaL_checkinteger(L: *mut lua_State, narg: c_int) -> lua_Integer {
let mut isnum = 0;
let int = lua_tointegerx(L, narg, &mut isnum);
if isnum == 0 {
luaL_typeerror(L, narg, lua_typename(L, LUA_TNUMBER));
}
int
}
pub unsafe fn luaL_optinteger(L: *mut lua_State, narg: c_int, def: lua_Integer) -> lua_Integer {
if lua_isnoneornil(L, narg) != 0 {
def
} else {
luaL_checkinteger(L, narg)
}
}
#[inline(always)]
pub unsafe fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int {
if luaL_getmetafield_(L, obj, e) != 0 {
+5 -3
View File
@@ -3,7 +3,7 @@
use std::os::raw::{c_char, c_float, c_int, c_void};
use std::ptr;
use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX};
use super::lua::{self, lua_CFunction, lua_Number, lua_State, lua_Unsigned, LUA_REGISTRYINDEX};
#[repr(C)]
pub struct luaL_Reg {
@@ -33,8 +33,10 @@ extern "C-unwind" {
pub fn luaL_checkboolean(L: *mut lua_State, narg: c_int) -> c_int;
pub fn luaL_optboolean(L: *mut lua_State, narg: c_int, def: c_int) -> c_int;
pub fn luaL_checkinteger(L: *mut lua_State, narg: c_int) -> lua_Integer;
pub fn luaL_optinteger(L: *mut lua_State, narg: c_int, def: lua_Integer) -> lua_Integer;
#[link_name = "luaL_checkinteger"]
pub fn luaL_checkinteger_(L: *mut lua_State, narg: c_int) -> c_int;
#[link_name = "luaL_optinteger"]
pub fn luaL_optinteger_(L: *mut lua_State, narg: c_int, def: c_int) -> c_int;
pub fn luaL_checkunsigned(L: *mut lua_State, narg: c_int) -> lua_Unsigned;
pub fn luaL_optunsigned(L: *mut lua_State, narg: c_int, def: lua_Unsigned) -> lua_Unsigned;
+12 -8
View File
@@ -69,8 +69,11 @@ pub const LUA_MINSTACK: c_int = 20;
/// A Lua number, usually equivalent to `f64`.
pub type lua_Number = c_double;
/// A Lua integer, equivalent to `i32`.
pub type lua_Integer = c_int;
/// A Lua integer, usually equivalent to `i64`
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
/// A Lua unsigned integer, equivalent to `u32`.
pub type lua_Unsigned = c_uint;
@@ -136,7 +139,7 @@ extern "C-unwind" {
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
#[link_name = "lua_tointegerx"]
pub fn lua_tointegerx_(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
pub fn lua_tointegerx_(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> c_int;
pub fn lua_tounsignedx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Unsigned;
pub fn lua_tovector(L: *mut lua_State, idx: c_int) -> *const c_float;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
@@ -160,7 +163,8 @@ extern "C-unwind" {
//
pub fn lua_pushnil(L: *mut lua_State);
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
#[link_name = "lua_pushinteger"]
pub fn lua_pushinteger_(L: *mut lua_State, n: c_int);
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
#[cfg(not(feature = "luau-vector4"))]
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float);
@@ -310,13 +314,13 @@ extern "C-unwind" {
//
#[inline(always)]
pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number {
lua_tonumberx(L, i, ptr::null_mut())
pub unsafe fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number {
lua_tonumberx(L, idx, ptr::null_mut())
}
#[inline(always)]
pub unsafe fn lua_tointeger_(L: *mut lua_State, i: c_int) -> lua_Integer {
lua_tointegerx_(L, i, ptr::null_mut())
pub unsafe fn lua_tointeger_(L: *mut lua_State, idx: c_int) -> c_int {
lua_tointegerx_(L, idx, ptr::null_mut())
}
#[inline(always)]
+5 -31
View File
@@ -808,15 +808,9 @@ macro_rules! lua_convert_int {
impl IntoLua for $x {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
cast(self)
Ok(cast(self)
.map(Value::Integer)
.or_else(|| cast(self).map(Value::Number))
// This is impossible error because conversion to Number never fails
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x).to_string(),
to: "number",
message: Some("out of range".to_owned()),
})
.unwrap_or_else(|| Value::Number(self as ffi::lua_Number)))
}
#[inline]
@@ -899,13 +893,7 @@ macro_rules! lua_convert_float {
impl IntoLua for $x {
#[inline]
fn into_lua(self, _: &Lua) -> Result<Value> {
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x).to_string(),
to: "number",
message: Some("out of range".to_string()),
})
.map(Value::Number)
Ok(Value::Number(self as _))
}
}
@@ -914,33 +902,19 @@ macro_rules! lua_convert_float {
fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
let ty = value.type_name();
lua.coerce_number(value)?
.map(|n| n as $x)
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x).to_string(),
message: Some("expected number or string coercible to number".to_string()),
})
.and_then(|n| {
cast(n).ok_or_else(|| Error::FromLuaConversionError {
from: ty,
to: stringify!($x).to_string(),
message: Some("number out of range".to_string()),
})
})
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let state = lua.state();
let type_id = ffi::lua_type(state, idx);
if type_id == ffi::LUA_TNUMBER {
let mut ok = 0;
let i = ffi::lua_tonumberx(state, idx, &mut ok);
if ok != 0 {
return cast(i).ok_or_else(|| Error::FromLuaConversionError {
from: "number",
to: stringify!($x).to_string(),
message: Some("out of range".to_owned()),
});
}
return Ok(ffi::lua_tonumber(state, idx) as _);
}
// Fallback to default
Self::from_lua(lua.stack_value(idx, Some(type_id)), lua.lua())
+1 -1
View File
@@ -51,7 +51,7 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
1
}
Ok("step") => {
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg);
let res = ffi::lua_gc(state, ffi::LUA_GCSTEP, arg as _);
ffi::lua_pushboolean(state, res);
1
}
+4 -1
View File
@@ -272,7 +272,10 @@ impl Value {
/// If the value is a Lua [`Integer`], try to convert it to `i64` or return `None` otherwise.
#[inline]
pub fn as_i64(&self) -> Option<i64> {
self.as_integer().map(i64::from)
#[cfg(target_pointer_width = "64")]
return self.as_integer();
#[cfg(not(target_pointer_width = "64"))]
return self.as_integer().map(i64::from);
}
/// Cast the value to `u64`.
+25
View File
@@ -483,6 +483,31 @@ fn test_panic() -> Result<()> {
Ok(())
}
#[cfg(target_pointer_width = "64")]
#[test]
fn test_safe_integers() -> Result<()> {
const MAX_SAFE_INTEGER: i64 = 2i64.pow(53) - 1;
const MIN_SAFE_INTEGER: i64 = -2i64.pow(53) + 1;
let lua = Lua::new();
let f = lua.load("return ...").into_function()?;
assert_eq!(f.call::<i64>(MAX_SAFE_INTEGER)?, MAX_SAFE_INTEGER);
assert_eq!(f.call::<i64>(MIN_SAFE_INTEGER)?, MIN_SAFE_INTEGER);
// For Lua versions that does not support 64-bit integers, the values will be converted to f64
#[cfg(any(feature = "luau", feature = "lua51", feature = "luajit"))]
{
assert_ne!(f.call::<i64>(MAX_SAFE_INTEGER + 2)?, MAX_SAFE_INTEGER + 2);
assert_ne!(f.call::<i64>(MIN_SAFE_INTEGER - 2)?, MIN_SAFE_INTEGER - 2);
let n = f.call::<i64>(i64::MAX)?;
println!("i64::MAX = {}", n);
}
Ok(())
}
#[test]
fn test_num_conversion() -> Result<()> {
let lua = Lua::new();