use crate::VmResult;
use crate::native::{NativeCallContext, NativeCallResult, NativeFunction};
use crate::number::str_to_long;
use crate::thread::Thread;
static INTEGER_LIB: [NativeFunction; 39] = [
NativeFunction {
name: "create",
function: int64_create,
},
NativeFunction {
name: "tonumber",
function: int64_tonumber,
},
NativeFunction {
name: "neg",
function: int64_neg,
},
NativeFunction {
name: "add",
function: int64_add,
},
NativeFunction {
name: "sub",
function: int64_sub,
},
NativeFunction {
name: "mul",
function: int64_mul,
},
NativeFunction {
name: "div",
function: int64_div,
},
NativeFunction {
name: "min",
function: int64_min,
},
NativeFunction {
name: "max",
function: int64_max,
},
NativeFunction {
name: "rem",
function: int64_rem,
},
NativeFunction {
name: "idiv",
function: int64_idiv,
},
NativeFunction {
name: "udiv",
function: int64_udiv,
},
NativeFunction {
name: "urem",
function: int64_urem,
},
NativeFunction {
name: "mod",
function: int64_mod,
},
NativeFunction {
name: "clamp",
function: int64_clamp,
},
NativeFunction {
name: "band",
function: int64_band,
},
NativeFunction {
name: "bor",
function: int64_bor,
},
NativeFunction {
name: "bnot",
function: int64_bnot,
},
NativeFunction {
name: "bxor",
function: int64_bxor,
},
NativeFunction {
name: "lt",
function: int64_lt,
},
NativeFunction {
name: "le",
function: int64_le,
},
NativeFunction {
name: "ult",
function: int64_ult,
},
NativeFunction {
name: "ule",
function: int64_ule,
},
NativeFunction {
name: "gt",
function: int64_gt,
},
NativeFunction {
name: "ge",
function: int64_ge,
},
NativeFunction {
name: "ugt",
function: int64_ugt,
},
NativeFunction {
name: "uge",
function: int64_uge,
},
NativeFunction {
name: "lshift",
function: int64_lshift,
},
NativeFunction {
name: "rshift",
function: int64_rshift,
},
NativeFunction {
name: "arshift",
function: int64_arshift,
},
NativeFunction {
name: "lrotate",
function: int64_lrotate,
},
NativeFunction {
name: "rrotate",
function: int64_rrotate,
},
NativeFunction {
name: "extract",
function: int64_extract,
},
NativeFunction {
name: "replace",
function: int64_replace,
},
NativeFunction {
name: "btest",
function: int64_btest,
},
NativeFunction {
name: "countrz",
function: int64_countrz,
},
NativeFunction {
name: "countlz",
function: int64_countlz,
},
NativeFunction {
name: "bswap",
function: int64_bswap,
},
NativeFunction {
name: "fromstring",
function: int64_from_string,
},
];
const MASK64: u64 = u64::MAX;
fn int64_create(ctx: NativeCallContext) -> NativeCallResult {
let thread = ctx.raw_thread();
unsafe {
let value = thread.check_number(1)?;
if (-9223372036854775808.0..9223372036854775808.0).contains(&value) {
let integer = value as i64;
if (integer as f64) == value {
thread.push_integer64(integer)?;
return Ok(1);
}
}
thread.push_nil()?;
Ok(1)
}
}
fn int64_from_string(ctx: NativeCallContext) -> NativeCallResult {
let thread = ctx.raw_thread();
unsafe {
let string = thread.check_string(1)?;
let base = thread.opt_integer(2, 10)?;
if !(2..=36).contains(&base) {
return thread
.lua_arg_error(2, "base out of range")
.map_err(Into::into);
}
if let Some(result) = str_to_long(string, base as u32) {
thread.push_integer64(result)?;
} else {
thread.push_nil()?;
}
}
Ok(1)
}
fn int64_tonumber(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_number(ctx.arg(1).integer64()? as f64)?;
Ok(1)
}
fn int64_neg(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_integer64((!(ctx.arg(1).integer64()? as u64)).wrapping_add(1) as i64)?;
Ok(1)
}
fn int64_add(ctx: NativeCallContext) -> NativeCallResult {
let x = ctx.arg(1).integer64()? as u64;
let y = ctx.arg(2).integer64()? as u64;
ctx.push_integer64(x.wrapping_add(y) as i64)?;
Ok(1)
}
fn int64_sub(ctx: NativeCallContext) -> NativeCallResult {
let x = ctx.arg(1).integer64()? as u64;
let y = ctx.arg(2).integer64()? as u64;
ctx.push_integer64(x.wrapping_sub(y) as i64)?;
Ok(1)
}
fn int64_mul(ctx: NativeCallContext) -> NativeCallResult {
let x = ctx.arg(1).integer64()? as u64;
let y = ctx.arg(2).integer64()? as u64;
ctx.push_integer64(x.wrapping_mul(y) as i64)?;
Ok(1)
}
fn int64_div(ctx: NativeCallContext) -> NativeCallResult {
let thread = ctx.raw_thread();
let a = ctx.arg(1).integer64()?;
let b = ctx.arg(2).integer64()?;
if b == 0 {
return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
}
if a == i64::MIN && b == -1 {
return unsafe { crate::error!(thread, "integer overflow") }.map_err(Into::into);
}
ctx.push_integer64(a / b)?;
Ok(1)
}
fn int64_idiv(ctx: NativeCallContext) -> NativeCallResult {
let thread = ctx.raw_thread();
let a = ctx.arg(1).integer64()?;
let b = ctx.arg(2).integer64()?;
if b == 0 {
return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
}
if a == i64::MIN && b == -1 {
return unsafe { crate::error!(thread, "integer overflow") }.map_err(Into::into);
}
let result = a / b;
ctx.push_integer64(if result < 0 && a % b != 0 {
result - 1
} else {
result
})?;
Ok(1)
}
fn int64_rem(ctx: NativeCallContext) -> NativeCallResult {
let thread = ctx.raw_thread();
let a = ctx.arg(1).integer64()?;
let b = ctx.arg(2).integer64()?;
if b == 0 {
return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
}
ctx.push_integer64(if a == i64::MIN && b == -1 { 0 } else { a % b })?;
Ok(1)
}
fn int64_mod(ctx: NativeCallContext) -> NativeCallResult {
let thread = ctx.raw_thread();
let a = ctx.arg(1).integer64()?;
let b = ctx.arg(2).integer64()?;
if b == 0 {
return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
}
let mut remainder = 0i64;
if a != i64::MIN || b != -1 {
remainder = a % b;
if remainder != 0 && ((a < 0) != (b < 0)) {
remainder += b;
}
}
ctx.push_integer64(remainder)?;
Ok(1)
}
fn int64_udiv(ctx: NativeCallContext) -> NativeCallResult {
let thread = ctx.raw_thread();
let a = ctx.arg(1).integer64()? as u64;
let b = ctx.arg(2).integer64()? as u64;
if b == 0 {
return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
}
ctx.push_integer64((a / b) as i64)?;
Ok(1)
}
fn int64_urem(ctx: NativeCallContext) -> NativeCallResult {
let thread = ctx.raw_thread();
let a = ctx.arg(1).integer64()? as u64;
let b = ctx.arg(2).integer64()? as u64;
if b == 0 {
return unsafe { crate::error!(thread, "division by zero") }.map_err(Into::into);
}
ctx.push_integer64((a % b) as i64)?;
Ok(1)
}
fn int64_min(ctx: NativeCallContext) -> NativeCallResult {
let mut result = ctx.arg(1).integer64()?;
for argument in ctx.args().skip(1) {
let value = argument.integer64()?;
if value < result {
result = value;
}
}
ctx.push_integer64(result)?;
Ok(1)
}
fn int64_max(ctx: NativeCallContext) -> NativeCallResult {
let mut result = ctx.arg(1).integer64()?;
for argument in ctx.args().skip(1) {
let value = argument.integer64()?;
if value > result {
result = value;
}
}
ctx.push_integer64(result)?;
Ok(1)
}
fn int64_band(ctx: NativeCallContext) -> NativeCallResult {
let mut result = u64::MAX;
for argument in ctx.args() {
result &= argument.integer64()? as u64;
}
ctx.push_integer64(result as i64)?;
Ok(1)
}
fn int64_bor(ctx: NativeCallContext) -> NativeCallResult {
let mut result = 0u64;
for argument in ctx.args() {
result |= argument.integer64()? as u64;
}
ctx.push_integer64(result as i64)?;
Ok(1)
}
fn int64_bnot(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_integer64((!(ctx.arg(1).integer64()? as u64)) as i64)?;
Ok(1)
}
fn int64_bxor(ctx: NativeCallContext) -> NativeCallResult {
let mut result = 0u64;
for argument in ctx.args() {
result ^= argument.integer64()? as u64;
}
ctx.push_integer64(result as i64)?;
Ok(1)
}
fn int64_lt(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_boolean(ctx.arg(1).integer64()? < ctx.arg(2).integer64()?)?;
Ok(1)
}
fn int64_le(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_boolean(ctx.arg(1).integer64()? <= ctx.arg(2).integer64()?)?;
Ok(1)
}
fn int64_ult(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_boolean((ctx.arg(1).integer64()? as u64) < (ctx.arg(2).integer64()? as u64))?;
Ok(1)
}
fn int64_ule(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_boolean((ctx.arg(1).integer64()? as u64) <= (ctx.arg(2).integer64()? as u64))?;
Ok(1)
}
fn int64_gt(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_boolean(ctx.arg(1).integer64()? > ctx.arg(2).integer64()?)?;
Ok(1)
}
fn int64_ge(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_boolean(ctx.arg(1).integer64()? >= ctx.arg(2).integer64()?)?;
Ok(1)
}
fn int64_ugt(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_boolean((ctx.arg(1).integer64()? as u64) > (ctx.arg(2).integer64()? as u64))?;
Ok(1)
}
fn int64_uge(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_boolean((ctx.arg(1).integer64()? as u64) >= (ctx.arg(2).integer64()? as u64))?;
Ok(1)
}
fn int64_lshift(ctx: NativeCallContext) -> NativeCallResult {
let n = ctx.arg(1).integer64()? as u64;
let shift = ctx.arg(2).integer64()?;
let result = if (-63..=63).contains(&shift) {
if shift < 0 {
n >> ((-shift) as u32)
} else {
n << (shift as u32)
}
} else {
0
};
ctx.push_integer64(result as i64)?;
Ok(1)
}
fn int64_rshift(ctx: NativeCallContext) -> NativeCallResult {
let n = ctx.arg(1).integer64()? as u64;
let shift = ctx.arg(2).integer64()?;
let result = if (-63..=63).contains(&shift) {
if shift < 0 {
n << ((-shift) as u32)
} else {
n >> (shift as u32)
}
} else {
0
};
ctx.push_integer64(result as i64)?;
Ok(1)
}
fn int64_arshift(ctx: NativeCallContext) -> NativeCallResult {
let n = ctx.arg(1).integer64()?;
let shift = ctx.arg(2).integer64()?;
let result = if (-63..=63).contains(&shift) {
if shift < 0 {
((n as u64) << ((-shift) as u32)) as i64
} else {
n >> (shift as u32)
}
} else if shift < -63 {
0
} else if n < 0 {
-1
} else {
0
};
ctx.push_integer64(result)?;
Ok(1)
}
fn int64_lrotate(ctx: NativeCallContext) -> NativeCallResult {
let n = ctx.arg(1).integer64()? as u64;
let shift = (ctx.arg(2).integer64()? as u64 % 64) as u32;
let result = if shift != 0 { n.rotate_left(shift) } else { n };
ctx.push_integer64(result as i64)?;
Ok(1)
}
fn int64_rrotate(ctx: NativeCallContext) -> NativeCallResult {
let n = ctx.arg(1).integer64()? as u64;
let shift = (ctx.arg(2).integer64()? as u64 % 64) as u32;
let result = if shift != 0 { n.rotate_right(shift) } else { n };
ctx.push_integer64(result as i64)?;
Ok(1)
}
fn field_args(ctx: &NativeCallContext, field_arg: i32, width_arg: i32) -> VmResult<(i64, i64)> {
let field = ctx.arg(field_arg).integer64()?;
let width = ctx.arg(width_arg).integer64_or(1)?;
if !(0..=63).contains(&field) {
return ctx
.arg(field_arg)
.error("field cannot be negative")
.map_err(Into::into);
}
if width <= 0 {
return ctx
.arg(width_arg)
.error("width must be positive")
.map_err(Into::into);
}
if field + width > 64 {
return ctx
.error("trying to access non-existent bits", [])
.map_err(Into::into);
}
Ok((field, width))
}
fn int64_extract(ctx: NativeCallContext) -> NativeCallResult {
let n = ctx.arg(1).integer64()? as u64;
let (field, width) = field_args(&ctx, 2, 3)?;
ctx.push_integer64(((n >> field) & (MASK64 >> (64 - width))) as i64)?;
Ok(1)
}
fn int64_replace(ctx: NativeCallContext) -> NativeCallResult {
let n = ctx.arg(1).integer64()? as u64;
let replacement = ctx.arg(2).integer64()? as u64;
let (field, width) = field_args(&ctx, 3, 4)?;
let base_mask = MASK64 >> (64 - width);
let replacement_bits = (replacement & base_mask) << field;
let mask = MASK64 ^ (base_mask << field);
ctx.push_integer64(((n & mask) | replacement_bits) as i64)?;
Ok(1)
}
fn int64_clamp(ctx: NativeCallContext) -> NativeCallResult {
let value = ctx.arg(1).integer64()?;
let min = ctx.arg(2).integer64()?;
let max = ctx.arg(3).integer64()?;
if min > max {
return ctx
.arg(3)
.error("max must be greater than or equal to min")
.map_err(Into::into);
}
ctx.push_integer64(if value < min {
min
} else if value > max {
max
} else {
value
})?;
Ok(1)
}
fn int64_btest(ctx: NativeCallContext) -> NativeCallResult {
let mut result = u64::MAX;
for argument in ctx.args() {
result &= argument.integer64()? as u64;
}
ctx.push_boolean(result != 0)?;
Ok(1)
}
fn int64_countrz(ctx: NativeCallContext) -> NativeCallResult {
let value = ctx.arg(1).integer64()? as u64;
ctx.push_integer64(if value == 0 {
64
} else {
value.trailing_zeros() as i64
})?;
Ok(1)
}
fn int64_countlz(ctx: NativeCallContext) -> NativeCallResult {
let value = ctx.arg(1).integer64()? as u64;
ctx.push_integer64(if value == 0 {
64
} else {
value.leading_zeros() as i64
})?;
Ok(1)
}
fn int64_bswap(ctx: NativeCallContext) -> NativeCallResult {
ctx.push_integer64((ctx.arg(1).integer64()? as u64).swap_bytes() as i64)?;
Ok(1)
}
impl Thread {
pub unsafe fn open_integer(&self) -> NativeCallResult {
unsafe { self.register(Some(super::LUA_INTLIB_NAME), &INTEGER_LIB[..])? };
unsafe {
self.push_integer64(i64::MAX)?;
self.raw_set_field(-2, "maxsigned")?;
self.push_integer64(i64::MIN)?;
self.raw_set_field(-2, "minsigned")?;
}
Ok(1)
}
}