use crate::runtime::Value;
use crate::vm::builtins::{arg_error, raise_str};
use crate::vm::error::LuaError;
use crate::vm::exec::Vm;
pub(crate) fn open_bit32(vm: &mut Vm) {
let t = vm.heap.new_table();
let set = |vm: &mut Vm, name: &str, f| {
let fv = vm.native(f);
let k = Value::Str(vm.heap.intern(name.as_bytes()));
unsafe { t.as_mut() }.set(&mut vm.heap, k, fv).expect("valid key");
};
set(vm, "band", b_band);
set(vm, "bor", b_bor);
set(vm, "bxor", b_bxor);
set(vm, "bnot", b_bnot);
set(vm, "btest", b_btest);
set(vm, "lshift", b_lshift);
set(vm, "rshift", b_rshift);
set(vm, "arshift", b_arshift);
set(vm, "lrotate", b_lrotate);
set(vm, "rrotate", b_rrotate);
set(vm, "extract", b_extract);
set(vm, "replace", b_replace);
vm.set_global("bit32", Value::Table(t)).expect("stdlib registration");
vm.barrier_back_table(t);
}
fn to_u32(vm: &mut Vm, v: Value, n: u32, who: &str) -> Result<u32, LuaError> {
let f = match v {
Value::Int(i) => i as f64,
Value::Float(f) => f,
Value::Str(s) => match crate::numeric::str2num(s.as_bytes(), true, true) {
Some(crate::numeric::Num::Int(i)) => i as f64,
Some(crate::numeric::Num::Float(f)) => f,
None => return Err(arg_error(vm, n, who, "number expected")),
},
_ => return Err(arg_error(vm, n, who, "number expected")),
};
if !f.is_finite() || f.fract() != 0.0 {
return Err(arg_error(vm, n, who, "number has no integer representation"));
}
let bits = if f >= 0.0 && f < (u64::MAX as f64) {
f as u64
} else {
(f as i64) as u64
};
Ok((bits & 0xFFFF_FFFF) as u32)
}
fn fold(
vm: &mut Vm,
fs: u32,
nargs: u32,
who: &str,
init: u32,
op: fn(u32, u32) -> u32,
) -> Result<u32, LuaError> {
let mut acc = init;
for i in 0..nargs {
let v = vm.nat_arg(fs, nargs, i);
let u = to_u32(vm, v, i + 1, who)?;
acc = op(acc, u);
}
Ok(vm.nat_return(fs, &[Value::Int(acc as i64)]))
}
fn b_band(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
fold(vm, fs, nargs, "band", 0xFFFF_FFFF, |a, b| a & b)
}
fn b_bor(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
fold(vm, fs, nargs, "bor", 0, |a, b| a | b)
}
fn b_bxor(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
fold(vm, fs, nargs, "bxor", 0, |a, b| a ^ b)
}
fn b_btest(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let mut acc: u32 = 0xFFFF_FFFF;
for i in 0..nargs {
let v = vm.nat_arg(fs, nargs, i);
let u = to_u32(vm, v, i + 1, "btest")?;
acc &= u;
}
Ok(vm.nat_return(fs, &[Value::Bool(acc != 0)]))
}
fn b_bnot(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let v = vm.nat_arg(fs, nargs, 0);
let u = to_u32(vm, v, 1, "bnot")?;
Ok(vm.nat_return(fs, &[Value::Int((!u) as i64)]))
}
fn signed_shift(x: u32, disp: i32) -> u32 {
let d = disp.unsigned_abs();
if d >= 32 {
0
} else if disp >= 0 {
x.wrapping_shl(d)
} else {
x.wrapping_shr(d)
}
}
fn shift_arg(vm: &mut Vm, fs: u32, nargs: u32, who: &str) -> Result<(u32, i32), LuaError> {
let v0 = vm.nat_arg(fs, nargs, 0);
let v1 = vm.nat_arg(fs, nargs, 1);
let x = to_u32(vm, v0, 1, who)?;
let d = vm.int_from(v1, "use as a number")?;
let d32 = if d > i32::MAX as i64 {
i32::MAX
} else if d < i32::MIN as i64 {
i32::MIN
} else {
d as i32
};
Ok((x, d32))
}
fn b_lshift(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let (x, d) = shift_arg(vm, fs, nargs, "lshift")?;
let r = signed_shift(x, d);
Ok(vm.nat_return(fs, &[Value::Int(r as i64)]))
}
fn b_rshift(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let (x, d) = shift_arg(vm, fs, nargs, "rshift")?;
let r = signed_shift(x, -d);
Ok(vm.nat_return(fs, &[Value::Int(r as i64)]))
}
fn b_arshift(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let (x, d) = shift_arg(vm, fs, nargs, "arshift")?;
let r = if d >= 0 {
let s = x as i32;
if d >= 32 {
if s < 0 { 0xFFFF_FFFF } else { 0 }
} else {
(s >> d) as u32
}
} else {
signed_shift(x, -d)
};
Ok(vm.nat_return(fs, &[Value::Int(r as i64)]))
}
fn rotate(x: u32, disp: i32) -> u32 {
let d = (disp.rem_euclid(32)) as u32;
x.rotate_left(d)
}
fn b_lrotate(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let (x, d) = shift_arg(vm, fs, nargs, "lrotate")?;
Ok(vm.nat_return(fs, &[Value::Int(rotate(x, d) as i64)]))
}
fn b_rrotate(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let (x, d) = shift_arg(vm, fs, nargs, "rrotate")?;
Ok(vm.nat_return(fs, &[Value::Int(rotate(x, -d) as i64)]))
}
fn field_args(
vm: &mut Vm,
fs: u32,
nargs: u32,
field_idx: u32,
who: &str,
) -> Result<(u32, u32), LuaError> {
let field = vm.int_from(vm.nat_arg(fs, nargs, field_idx), "use as a number")?;
let width = if nargs > field_idx + 1 {
vm.int_from(vm.nat_arg(fs, nargs, field_idx + 1), "use as a number")?
} else {
1
};
if !(0..=31).contains(&field) {
return Err(arg_error(vm, field_idx + 1, who, "field out of range"));
}
if !(1..=32).contains(&width) {
return Err(arg_error(vm, field_idx + 2, who, "trying to access non-existent bits"));
}
if field + width > 32 {
return Err(arg_error(vm, field_idx + 1, who, "trying to access non-existent bits"));
}
Ok((field as u32, width as u32))
}
fn b_extract(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let n = to_u32(vm, vm.nat_arg(fs, nargs, 0), 1, "extract")?;
let (f, w) = field_args(vm, fs, nargs, 1, "extract")?;
let mask = if w == 32 { 0xFFFF_FFFF } else { (1u32 << w) - 1 };
let r = (n >> f) & mask;
Ok(vm.nat_return(fs, &[Value::Int(r as i64)]))
}
fn b_replace(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let n = to_u32(vm, vm.nat_arg(fs, nargs, 0), 1, "replace")?;
let v = to_u32(vm, vm.nat_arg(fs, nargs, 1), 2, "replace")?;
let (f, w) = field_args(vm, fs, nargs, 2, "replace")?;
let mask = if w == 32 { 0xFFFF_FFFF } else { (1u32 << w) - 1 };
let r = (n & !(mask << f)) | ((v & mask) << f);
Ok(vm.nat_return(fs, &[Value::Int(r as i64)]))
}
#[allow(dead_code)]
fn _keep(_vm: &mut Vm) -> LuaError {
raise_str(_vm, "unused")
}