Skip to main content

luaur_vm/functions/
lua_v_concat.rs

1//! Node: `cxx:Function:Luau.VM:VM/src/lvmutils.cpp:399:luaV_concat`
2//! Source: `VM/src/lvmutils.cpp:399-461` (hand-ported)
3
4use crate::functions::call_bin_tm::call_bin_tm;
5use crate::functions::lua_g_concaterror::lua_g_concaterror;
6use crate::functions::lua_s_buffinish::luaS_buffinish;
7use crate::functions::lua_s_bufstart::luaS_bufstart;
8use crate::functions::lua_s_newlstr::luaS_newlstr;
9use crate::macros::lua_buffersize::LUA_BUFFERSIZE;
10use crate::macros::lua_g_runerror::lua_g_runerror;
11use crate::macros::maxssize::MAXSSIZE;
12use crate::macros::setsvalue::setsvalue;
13use crate::macros::svalue::svalue;
14use crate::macros::tostring::tostring;
15use crate::macros::tsvalue::tsvalue;
16use crate::macros::ttisnumber::ttisnumber;
17use crate::macros::ttisstring::ttisstring;
18use crate::records::t_string::TString;
19use crate::type_aliases::lua_state::lua_State;
20use crate::type_aliases::stk_id::StkId;
21use crate::type_aliases::tms::TMS;
22use core::ffi::c_char;
23
24#[allow(non_snake_case)]
25pub unsafe fn lua_v_concat(L: *mut lua_State, mut total: i32, mut last: i32) {
26    loop {
27        let top: StkId = (*L).base.add((last + 1) as usize);
28        let mut n = 2; // number of elements handled in this pass (at least 2)
29        if !(ttisstring!(top.sub(2)) || ttisnumber!(top.sub(2))) || !tostring!(L, top.sub(1)) {
30            if call_bin_tm(L, top.sub(2), top.sub(1), top.sub(2), TMS::TM_CONCAT) == 0 {
31                lua_g_concaterror(L, top.sub(2), top.sub(1));
32            }
33        } else if (*tsvalue!(top.sub(1))).len == 0 {
34            // second op is empty? result is first op (as string)
35            let _ = tostring!(L, top.sub(2));
36        } else {
37            // at least two string values; get as many as possible
38            let mut tl = (*tsvalue!(top.sub(1))).len as usize;
39            // collect total length
40            n = 1;
41            while n < total && tostring!(L, top.sub((n + 1) as usize)) {
42                let l = (*tsvalue!(top.sub((n + 1) as usize))).len as usize;
43                if l > MAXSSIZE as usize - tl {
44                    lua_g_runerror!(L, "string length overflow");
45                }
46                tl += l;
47                n += 1;
48            }
49
50            let mut buf = [0 as c_char; LUA_BUFFERSIZE as usize];
51            let mut ts: *mut TString = core::ptr::null_mut();
52
53            let buffer: *mut c_char = if tl < LUA_BUFFERSIZE as usize {
54                buf.as_mut_ptr()
55            } else {
56                ts = luaS_bufstart(L, tl);
57                (*ts).data.as_mut_ptr()
58            };
59
60            // concat all strings
61            tl = 0;
62            let mut i = n;
63            while i > 0 {
64                let l = (*tsvalue!(top.sub(i as usize))).len as usize;
65                core::ptr::copy_nonoverlapping(svalue!(top.sub(i as usize)), buffer.add(tl), l);
66                tl += l;
67                i -= 1;
68            }
69
70            if tl < LUA_BUFFERSIZE as usize {
71                setsvalue!(L, top.sub(n as usize), luaS_newlstr(L, buffer, tl));
72            } else {
73                setsvalue!(L, top.sub(n as usize), luaS_buffinish(L, ts));
74            }
75        }
76        total -= n - 1; // got `n` strings to create 1 new
77        last -= n - 1;
78        if total <= 1 {
79            break; // repeat until only 1 result left
80        }
81    }
82}
83
84#[export_name = "luaV_concat"]
85pub unsafe extern "C" fn lua_v_concat_export(L: *mut lua_State, total: i32, last: i32) {
86    lua_v_concat(L, total, last);
87}
88
89#[allow(non_snake_case, unused_imports)]
90pub use lua_v_concat as luaV_concat;