use mlua::{Lua, Table};
const MAGIC: f64 = 6_755_399_441_055_744.0;
fn tobit(x: f64) -> i32 {
(x + MAGIC).to_bits() as u32 as i32
}
fn tohex(x: i32, n: i32) -> String {
let upper = n < 0;
let digits = usize::try_from(n.saturating_abs()).unwrap_or(8).min(8);
let word = x as u32;
let mut out = String::with_capacity(digits);
for i in (0..digits).rev() {
let nibble = (word >> (i * 4)) & 0xf;
let digit = char::from_digit(nibble, 16).unwrap_or('0');
out.push(if upper {
digit.to_ascii_uppercase()
} else {
digit
});
}
out
}
pub(super) fn statics(lua: &Lua, raw: &Table) -> mlua::Result<()> {
raw.raw_set("bit_tobit", lua.create_function(|_, x: f64| Ok(tobit(x)))?)?;
raw.raw_set("bit_bnot", lua.create_function(|_, x: f64| Ok(!tobit(x)))?)?;
raw.raw_set(
"bit_bswap",
lua.create_function(|_, x: f64| Ok(tobit(x).swap_bytes()))?,
)?;
raw.raw_set(
"bit_tohex",
lua.create_function(|_, (x, n): (f64, f64)| Ok(tohex(tobit(x), tobit(n))))?,
)?;
raw.raw_set(
"bit_band",
lua.create_function(|_, (a, b): (f64, f64)| Ok(tobit(a) & tobit(b)))?,
)?;
raw.raw_set(
"bit_bor",
lua.create_function(|_, (a, b): (f64, f64)| Ok(tobit(a) | tobit(b)))?,
)?;
raw.raw_set(
"bit_bxor",
lua.create_function(|_, (a, b): (f64, f64)| Ok(tobit(a) ^ tobit(b)))?,
)?;
raw.raw_set(
"bit_lshift",
lua.create_function(|_, (x, n): (f64, f64)| Ok(((tobit(x) as u32) << count(n)) as i32))?,
)?;
raw.raw_set(
"bit_rshift",
lua.create_function(|_, (x, n): (f64, f64)| Ok(((tobit(x) as u32) >> count(n)) as i32))?,
)?;
raw.raw_set(
"bit_arshift",
lua.create_function(|_, (x, n): (f64, f64)| Ok(tobit(x) >> count(n)))?,
)?;
raw.raw_set(
"bit_rol",
lua.create_function(|_, (x, n): (f64, f64)| Ok(tobit(x).rotate_left(count(n))))?,
)?;
raw.raw_set(
"bit_ror",
lua.create_function(|_, (x, n): (f64, f64)| Ok(tobit(x).rotate_right(count(n))))?,
)?;
Ok(())
}
fn count(n: f64) -> u32 {
tobit(n) as u32 & 31
}
#[cfg(test)]
mod tests {
use super::{tobit, tohex};
#[test]
fn a_number_comes_down_to_a_word_the_way_lua_bit_op_does_it() {
for (given, want) in [
(1.0, 1),
(-1.0, -1),
(0.0, 0),
(4_294_967_295.0, -1),
(4_294_967_296.0, 0),
(4_294_967_297.0, 1),
(2_147_483_648.0, i32::MIN),
(-2_147_483_648.0, i32::MIN),
(9_007_199_254_740_992.0, 0),
(1.5, 2),
(2.5, 2),
(0.5, 0),
(-0.5, 0),
(-1.5, -2),
(1.4, 1),
(1.6, 2),
] {
assert_eq!(tobit(given), want, "{given}");
}
}
#[test]
fn a_word_is_written_out_as_the_digits_that_were_asked_for() {
for (word, digits, want) in [
(1, 8, "00000001"),
(-1, 8, "ffffffff"),
(255, 2, "ff"),
(255, -8, "000000FF"),
(-1, -8, "FFFFFFFF"),
(0x8765_4321_u32 as i32, 4, "4321"),
(1, 0, ""),
(1, 9, "00000001"),
(1, -9, "00000001"),
(1, i32::MIN, "00000001"),
] {
assert_eq!(tohex(word, digits), want, "{word} {digits}");
}
}
}