Skip to main content

luaur_vm/functions/
lua_o_pushvfstring.rs

1use crate::macros::incr_top::incr_top;
2use crate::macros::lua_buffersize::LUA_BUFFERSIZE;
3use crate::macros::lua_s_new::luaS_new;
4use crate::macros::setsvalue::setsvalue;
5use crate::macros::svalue::svalue;
6use crate::type_aliases::lua_state::lua_State;
7use core::ffi::c_char;
8use core::ffi::c_int;
9
10#[allow(non_snake_case)]
11pub fn luaO_pushvfstring(
12    L: *mut lua_State,
13    _fmt: *const c_char,
14    args: core::fmt::Arguments<'_>,
15) -> *const c_char {
16    // Luau VM uses a fixed-size buffer for string formatting in luaO_pushvfstring.
17    // Since we are translating to Rust's core::fmt::Arguments, we use a stack buffer
18    // and a custom writer to mimic vsnprintf behavior.
19    let mut buffer = [0u8; LUA_BUFFERSIZE as usize];
20    let mut writer = BufferWriter {
21        buf: &mut buffer,
22        pos: 0,
23    };
24
25    let _ = core::fmt::write(&mut writer, args);
26
27    // Ensure null termination for luaS_new which expects const char*
28    let len = writer.pos;
29    if len < buffer.len() {
30        buffer[len] = 0;
31    } else {
32        buffer[buffer.len() - 1] = 0;
33    }
34
35    unsafe {
36        // The macro setsvalue! expects a pointer to TValue. (*L).top is a StkId (TValue*).
37        setsvalue!(L, (*L).top, luaS_new(L, buffer.as_ptr() as *const c_char));
38
39        // The previous attempt failed because the incr_top! macro expansion encountered
40        // name mismatches (luaD_growstack vs lua_d_growstack) and field access errors
41        // (stacksize vs stacksize). We manually perform the logic here to ensure
42        // compatibility with the translated records and functions.
43
44        // luaD_checkstack(L, 1);
45        let n = 1;
46        let stack_last = (*L).stack_last as *mut u8;
47        let top = (*L).top as *mut u8;
48        let limit_reached = (stack_last as usize).wrapping_sub(top as usize)
49            <= (n as usize * core::mem::size_of::<crate::type_aliases::t_value::TValue>());
50
51        if limit_reached {
52            crate::functions::lua_d_growstack::lua_d_growstack(L, n);
53        } else {
54            // condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK, 0));
55            // In the Rust port, we call the snake_case function.
56            // Note: lua_d_reallocstack in this crate is currently a stub with no arguments.
57            type LuaDReallocStackFn = unsafe fn(*mut lua_State, c_int, c_int);
58            let realloc_stack: LuaDReallocStackFn = core::mem::transmute(
59                crate::functions::lua_d_reallocstack::lua_d_reallocstack as *const (),
60            );
61            realloc_stack(
62                L,
63                (*L).stacksize - crate::macros::extra_stack::EXTRA_STACK,
64                0,
65            );
66        }
67
68        // L->top++;
69        (*L).top = (*L).top.add(1);
70
71        // svalue! expects a pointer to TValue.
72        svalue!((*L).top.offset(-1))
73    }
74}
75
76struct BufferWriter<'a> {
77    buf: &'a mut [u8],
78    pos: usize,
79}
80
81impl<'a> core::fmt::Write for BufferWriter<'a> {
82    fn write_str(&mut self, s: &str) -> core::fmt::Result {
83        let bytes = s.as_bytes();
84        let remain = self.buf.len().saturating_sub(self.pos);
85        let to_copy = core::cmp::min(remain, bytes.len());
86        if to_copy > 0 {
87            self.buf[self.pos..self.pos + to_copy].copy_from_slice(&bytes[..to_copy]);
88            self.pos += to_copy;
89        }
90        Ok(())
91    }
92}