use crate::state_stub::{LuaState, LuaStateStubExt as _};
use lua_types::{LuaError, LuaValue};
type LuaCFunction = fn(&mut LuaState) -> Result<usize, LuaError>;
fn arg_u32(state: &mut LuaState, arg: i32) -> Result<u32, LuaError> {
let n = state.check_integer(arg)?;
Ok(n as u32)
}
fn push_u32(state: &mut LuaState, v: u32) {
state.push(LuaValue::Int(v as i64));
}
fn fold(
state: &mut LuaState,
init: u32,
op: fn(u32, u32) -> u32,
) -> Result<usize, LuaError> {
let top = state.get_top();
let mut acc = init;
for i in 1..=top {
acc = op(acc, arg_u32(state, i)?);
}
push_u32(state, acc & 0xFFFF_FFFF);
Ok(1)
}
fn bit_band(state: &mut LuaState) -> Result<usize, LuaError> {
fold(state, 0xFFFF_FFFF, |a, b| a & b)
}
fn bit_bor(state: &mut LuaState) -> Result<usize, LuaError> {
fold(state, 0, |a, b| a | b)
}
fn bit_bxor(state: &mut LuaState) -> Result<usize, LuaError> {
fold(state, 0, |a, b| a ^ b)
}
fn bit_bnot(state: &mut LuaState) -> Result<usize, LuaError> {
let a = arg_u32(state, 1)?;
push_u32(state, !a);
Ok(1)
}
fn bit_lshift(state: &mut LuaState) -> Result<usize, LuaError> {
let a = arg_u32(state, 1)?;
let disp = state.check_integer(2)?;
push_u32(state, shift(a, disp));
Ok(1)
}
fn bit_rshift(state: &mut LuaState) -> Result<usize, LuaError> {
let a = arg_u32(state, 1)?;
let disp = state.check_integer(2)?;
push_u32(state, shift(a, -disp));
Ok(1)
}
fn shift(x: u32, disp: i64) -> u32 {
if disp <= -32 || disp >= 32 {
0
} else if disp >= 0 {
x << disp
} else {
x >> (-disp)
}
}
fn mask_w(w: u32) -> u32 {
if w >= 32 {
0xFFFF_FFFF
} else {
(1u32 << w) - 1
}
}
fn field_args(
state: &mut LuaState,
field_arg: i32,
width_arg: i32,
) -> Result<(u32, u32), LuaError> {
let f = state.check_integer(field_arg)?;
let w = if state.get_top() >= width_arg {
state.check_integer(width_arg)?
} else {
1
};
if f < 0 {
return Err(LuaError::arg_error(field_arg, "field cannot be negative"));
}
if w < 1 {
return Err(LuaError::arg_error(width_arg, "width must be positive"));
}
if f + w > 32 {
return Err(LuaError::arg_error(
field_arg,
"trying to access non-existent bits",
));
}
Ok((f as u32, w as u32))
}
fn bit_btest(state: &mut LuaState) -> Result<usize, LuaError> {
let top = state.get_top();
let mut acc: u32 = 0xFFFF_FFFF;
for i in 1..=top {
acc &= arg_u32(state, i)?;
}
state.push(LuaValue::Bool(acc != 0));
Ok(1)
}
fn bit_extract(state: &mut LuaState) -> Result<usize, LuaError> {
let n = arg_u32(state, 1)?;
let (f, w) = field_args(state, 2, 3)?;
push_u32(state, (n >> f) & mask_w(w));
Ok(1)
}
fn bit_replace(state: &mut LuaState) -> Result<usize, LuaError> {
let n = arg_u32(state, 1)?;
let v = arg_u32(state, 2)?;
let (f, w) = field_args(state, 3, 4)?;
let m = mask_w(w);
push_u32(state, (n & !(m << f)) | ((v & m) << f));
Ok(1)
}
fn bit_arshift(state: &mut LuaState) -> Result<usize, LuaError> {
let x = arg_u32(state, 1)?;
let disp = state.check_integer(2)?;
let r = if disp < 0 {
shift(x, -disp)
} else if disp >= 32 {
if x & 0x8000_0000 != 0 {
0xFFFF_FFFF
} else {
0
}
} else if x & 0x8000_0000 != 0 {
(x >> disp) | !(0xFFFF_FFFFu32 >> disp)
} else {
x >> disp
};
push_u32(state, r);
Ok(1)
}
fn rotate(x: u32, disp: i64) -> u32 {
let d = (((disp % 32) + 32) % 32) as u32;
if d == 0 {
x
} else {
(x << d) | (x >> (32 - d))
}
}
fn bit_lrotate(state: &mut LuaState) -> Result<usize, LuaError> {
let x = arg_u32(state, 1)?;
let disp = state.check_integer(2)?;
push_u32(state, rotate(x, disp));
Ok(1)
}
fn bit_rrotate(state: &mut LuaState) -> Result<usize, LuaError> {
let x = arg_u32(state, 1)?;
let disp = state.check_integer(2)?;
push_u32(state, rotate(x, -disp));
Ok(1)
}
const BIT32_FUNCS: &[(&[u8], LuaCFunction)] = &[
(b"band", bit_band),
(b"bor", bit_bor),
(b"bxor", bit_bxor),
(b"bnot", bit_bnot),
(b"lshift", bit_lshift),
(b"rshift", bit_rshift),
(b"btest", bit_btest),
(b"extract", bit_extract),
(b"replace", bit_replace),
(b"arshift", bit_arshift),
(b"lrotate", bit_lrotate),
(b"rrotate", bit_rrotate),
];
pub fn open_bit32(state: &mut LuaState) -> Result<usize, LuaError> {
state.new_lib(BIT32_FUNCS)?;
Ok(1)
}