Skip to main content

luaur_vm/functions/
buffer_readbits.rs

1use luaur_common::macros::luau_big_endian::LUAU_BIG_ENDIAN;
2
3use crate::functions::lua_l_checkbuffer::lua_l_checkbuffer;
4use crate::functions::lua_l_checkinteger::lua_l_checkinteger;
5use crate::functions::lua_l_checknumber::lua_l_checknumber;
6use crate::macros::lua_l_error::luaL_error;
7use crate::type_aliases::lua_state::lua_State;
8
9pub fn buffer_readbits(L: *mut lua_State) -> core::ffi::c_int {
10    let mut len: usize = 0;
11    let buf = lua_l_checkbuffer(L, 1, &mut len) as *mut core::ffi::c_char;
12    let bitoffset = lua_l_checknumber(L, 2) as i64;
13    let bitcount = lua_l_checkinteger(L, 3);
14
15    if bitoffset < 0 {
16        luaL_error!(L, "buffer access out of bounds");
17    }
18
19    if (bitcount as u32) > 32 {
20        luaL_error!(L, "bit count is out of range of [0; 32]");
21    }
22
23    if (bitoffset as u64 + bitcount as u64) > (len as u64) * 8 {
24        luaL_error!(L, "buffer access out of bounds");
25    }
26
27    let startbyte = (bitoffset / 8) as u32;
28    let endbyte = ((bitoffset + bitcount as i64 + 7) / 8) as u32;
29
30    let mut data: u64 = 0;
31
32    if LUAU_BIG_ENDIAN {
33        for i in (startbyte as i32..=endbyte as i32 - 1).rev() {
34            data = (data << 8) + (unsafe { *buf.add(i as usize) } as u8) as u64;
35        }
36    } else {
37        let src = unsafe { buf.add(startbyte as usize) };
38        let dst = &mut data as *mut u64 as *mut core::ffi::c_char;
39        unsafe {
40            core::ptr::copy_nonoverlapping(src, dst, (endbyte - startbyte) as usize);
41        }
42    }
43
44    let subbyteoffset = (bitoffset & 0x7) as u64;
45    let mask = (1u64 << bitcount as u64) - 1;
46
47    let result = ((data >> subbyteoffset) & mask) as u32;
48    crate::functions::lua_pushunsigned::lua_pushunsigned(L, result);
49
50    1
51}