use crate::runtime::Value;
use crate::vm::builtins::{arg_error, check_table, check_table_at, raise_str};
use crate::vm::error::LuaError;
use crate::vm::exec::Vm;
pub(crate) fn open_table(vm: &mut Vm) {
let t = vm.heap.new_table();
let set = |vm: &mut Vm, name: &str, f| {
let fv = vm.native(f);
let k = Value::Str(vm.heap.intern(name.as_bytes()));
unsafe { t.as_mut() }.set(&mut vm.heap, k, fv).expect("valid key");
};
set(vm, "insert", t_insert);
set(vm, "remove", t_remove);
set(vm, "concat", t_concat);
set(vm, "unpack", t_unpack);
set(vm, "pack", t_pack);
set(vm, "move", t_move);
set(vm, "create", t_create);
set(vm, "sort", t_sort);
if vm.version() == crate::version::LuaVersion::Lua51 {
set(vm, "getn", t_getn);
set(vm, "foreach", t_foreach);
set(vm, "foreachi", t_foreachi);
set(vm, "maxn", t_maxn);
}
vm.set_global("table", Value::Table(t)).expect("stdlib registration");
vm.barrier_back_table(t);
}
fn t_getn(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let tv = vm.nat_arg(fs, nargs, 0);
check_table(vm, tv, "getn")?;
let n = vm.checked_len(tv)?;
Ok(vm.nat_return(fs, &[Value::Int(n)]))
}
fn t_foreach(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let tv = vm.nat_arg(fs, nargs, 0);
let t = check_table(vm, tv, "foreach")?;
let f = vm.nat_arg(fs, nargs, 1);
let mut key = Value::Nil;
loop {
match t.next(key).map_err(|_| raise_str(vm, "invalid key to 'next'"))? {
Some((k, v)) => {
let rs = vm.call_value(f, &[k, v])?;
if let Some(r) = rs.first().copied()
&& !r.is_nil()
{
return Ok(vm.nat_return(fs, &[r]));
}
key = k;
}
None => return Ok(vm.nat_return(fs, &[Value::Nil])),
}
}
}
fn t_foreachi(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let tv = vm.nat_arg(fs, nargs, 0);
check_table(vm, tv, "foreachi")?;
let f = vm.nat_arg(fs, nargs, 1);
let n = vm.checked_len(tv)?;
for i in 1..=n {
let v = vm.index_value(tv, Value::Int(i))?;
let rs = vm.call_value(f, &[Value::Int(i), v])?;
if let Some(r) = rs.first().copied()
&& !r.is_nil()
{
return Ok(vm.nat_return(fs, &[r]));
}
}
Ok(vm.nat_return(fs, &[Value::Nil]))
}
fn t_maxn(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let tv = vm.nat_arg(fs, nargs, 0);
let t = check_table(vm, tv, "maxn")?;
let mut max: f64 = 0.0;
let mut key = Value::Nil;
loop {
let entry = t
.next(key)
.map_err(|_| raise_str(vm, "invalid key to 'next'"))?;
match entry {
Some((k, _)) => {
if let Some(n) = match k {
Value::Int(i) => Some(i as f64),
Value::Float(f) => Some(f),
_ => None,
} && n > max
{
max = n;
}
key = k;
}
None => break,
}
}
Ok(vm.nat_return(fs, &[Value::Float(max)]))
}
fn t_insert(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let tv = vm.nat_arg(fs, nargs, 0);
check_table(vm, tv, "insert")?;
let n = vm.checked_len(tv)?;
let e = n.wrapping_add(1);
let (pos, v) = match nargs {
2 => (e, vm.nat_arg(fs, nargs, 1)),
3 => {
let pos = vm.int_from(vm.nat_arg(fs, nargs, 1), "use as a position")?;
if (pos as u64).wrapping_sub(1) >= e as u64 {
return Err(arg_error(vm, 2, "insert", "position out of bounds"));
}
let mut i = e;
while i > pos {
let mv = vm.index_value(tv, Value::Int(i - 1))?;
vm.newindex_value(tv, Value::Int(i), mv)?;
i -= 1;
}
(pos, vm.nat_arg(fs, nargs, 2))
}
_ => return Err(raise_str(vm, "wrong number of arguments to 'insert'")),
};
vm.newindex_value(tv, Value::Int(pos), v)?;
Ok(0)
}
fn t_remove(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let tv = vm.nat_arg(fs, nargs, 0);
check_table(vm, tv, "remove")?;
let n = vm.checked_len(tv)?;
let pos = if nargs >= 2 {
let pos = vm.int_from(vm.nat_arg(fs, nargs, 1), "use as a position")?;
if n > 0 && (pos < 1 || pos > n + 1) {
return Err(arg_error(vm, 2, "remove", "position out of bounds"));
}
pos
} else {
n
};
let removed = vm.index_value(tv, Value::Int(pos))?;
if pos <= n {
let mut i = pos;
while i < n {
let mv = vm.index_value(tv, Value::Int(i + 1))?;
vm.newindex_value(tv, Value::Int(i), mv)?;
i += 1;
}
vm.newindex_value(tv, Value::Int(n), Value::Nil)?;
}
Ok(vm.nat_return(fs, &[removed]))
}
fn t_concat(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let tv = vm.nat_arg(fs, nargs, 0);
check_table(vm, tv, "concat")?;
let legacy_float = vm.version() <= crate::version::LuaVersion::Lua52;
let sep: Vec<u8> = match vm.nat_arg(fs, nargs, 1) {
Value::Nil => Vec::new(),
Value::Str(s) => s.as_bytes().to_vec(),
Value::Int(i) => crate::numeric::num_to_string(crate::numeric::Num::Int(i)).into_bytes(),
Value::Float(f) => {
crate::numeric::num_to_string_for(crate::numeric::Num::Float(f), legacy_float)
.into_bytes()
}
v => {
return Err(arg_error(
vm,
2,
"concat",
&format!("string expected, got {}", v.type_name()),
));
}
};
let i = if nargs >= 3 {
vm.int_from(vm.nat_arg(fs, nargs, 2), "use as an index")?
} else {
1
};
let j = if nargs >= 4 {
vm.int_from(vm.nat_arg(fs, nargs, 3), "use as an index")?
} else {
vm.checked_len(tv)?
};
let mut out: Vec<u8> = Vec::new();
let mut k = i;
while k < j {
concat_field(vm, tv, k, &mut out, legacy_float)?;
out.extend_from_slice(&sep);
k += 1;
}
if i <= j {
concat_field(vm, tv, j, &mut out, legacy_float)?;
}
let s = Value::Str(vm.heap.intern(&out));
Ok(vm.nat_return(fs, &[s]))
}
fn concat_field(
vm: &mut Vm,
tv: Value,
k: i64,
out: &mut Vec<u8>,
legacy_float: bool,
) -> Result<(), LuaError> {
match vm.index_value(tv, Value::Int(k))? {
Value::Str(s) => out.extend_from_slice(s.as_bytes()),
Value::Int(x) => {
let mut buf = [0u8; 20];
out.extend_from_slice(crate::numeric::write_i64_dec(x, &mut buf))
}
Value::Float(x) => out.extend_from_slice(
crate::numeric::num_to_string_for(crate::numeric::Num::Float(x), legacy_float)
.as_bytes(),
),
_ => {
return Err(raise_str(
vm,
&format!("invalid value (at index {k}) in table for 'concat'"),
));
}
}
Ok(())
}
const MAX_UNPACK: i64 = 1_000_000;
pub(crate) fn t_unpack(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let tv = vm.nat_arg(fs, nargs, 0);
check_table(vm, tv, "unpack")?;
let i = if nargs >= 2 && !vm.nat_arg(fs, nargs, 1).is_nil() {
vm.int_from(vm.nat_arg(fs, nargs, 1), "use as an index")?
} else {
1
};
let j = if nargs >= 3 && !vm.nat_arg(fs, nargs, 2).is_nil() {
vm.int_from(vm.nat_arg(fs, nargs, 2), "use as an index")?
} else {
vm.checked_len(tv)?
};
if i > j {
return Ok(0);
}
let count = (j as i128) - (i as i128) + 1;
if count >= MAX_UNPACK as i128 || count + 1 > vm.stack_room() as i128 {
return Err(raise_str(vm, "too many results to unpack"));
}
let mut vals: Vec<Value> = Vec::with_capacity(count as usize);
for k in i..=j {
vals.push(vm.index_value(tv, Value::Int(k))?);
}
Ok(vm.nat_return(fs, &vals))
}
fn t_pack(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let t = vm.heap.new_table();
{
let tm = unsafe { t.as_mut() };
for i in 0..nargs {
let v = vm.nat_arg(fs, nargs, i);
let _ = tm.set_int(&mut vm.heap, i as i64 + 1, v);
}
}
let nk = Value::Str(vm.heap.intern(b"n"));
unsafe { t.as_mut() }
.set(&mut vm.heap, nk, Value::Int(nargs as i64))
.expect("valid key");
vm.barrier_back_table(t);
Ok(vm.nat_return(fs, &[Value::Table(t)]))
}
fn t_move(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let a1v = vm.nat_arg(fs, nargs, 0);
let a1 = check_table(vm, a1v, "move")?;
let f = vm.int_from(vm.nat_arg(fs, nargs, 1), "use as an index")?;
let e = vm.int_from(vm.nat_arg(fs, nargs, 2), "use as an index")?;
let d = vm.int_from(vm.nat_arg(fs, nargs, 3), "use as an index")?;
let (a2v, a2) = if nargs >= 5 {
let v = vm.nat_arg(fs, nargs, 4);
(v, check_table_at(vm, v, 5, "move")?)
} else {
(a1v, a1)
};
if e >= f {
if !(f > 0 || (e as i128) < i64::MAX as i128 + f as i128) {
return Err(arg_error(vm, 3, "move", "too many elements to move"));
}
let n = e as i128 - f as i128 + 1;
if (d as i128) > i64::MAX as i128 - n + 1 {
return Err(arg_error(vm, 4, "move", "destination wrap around"));
}
if d > f && d <= e && a1.ptr_eq(a2) {
let mut i = e;
while i >= f {
let v = vm.index_value(a1v, Value::Int(i))?;
vm.newindex_value(a2v, Value::Int(d + (i - f)), v)?;
i -= 1;
}
} else {
for i in f..=e {
let v = vm.index_value(a1v, Value::Int(i))?;
vm.newindex_value(a2v, Value::Int(d + (i - f)), v)?;
}
}
}
Ok(vm.nat_return(fs, &[a2v]))
}
fn t_create(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let n = vm.int_from(vm.nat_arg(fs, nargs, 0), "use as a size")?;
if !(0..=i32::MAX as i64).contains(&n) {
return Err(arg_error(vm, 1, "create", "out of range"));
}
let m = match vm.nat_arg(fs, nargs, 1) {
Value::Nil => 0,
v => vm.int_from(v, "use as a size")?,
};
if !(0..=i32::MAX as i64).contains(&m) {
return Err(arg_error(vm, 2, "create", "out of range"));
}
if m > (1 << 30) {
return Err(raise_str(vm, "table overflow"));
}
let t = vm.heap.new_table();
unsafe { t.as_mut() }.ensure_array(&mut vm.heap, n as usize);
unsafe { t.as_mut() }.ensure_hash(&mut vm.heap, m as usize);
Ok(vm.nat_return(fs, &[Value::Table(t)]))
}
fn t_sort(vm: &mut Vm, fs: u32, nargs: u32) -> Result<u32, LuaError> {
let tv = vm.nat_arg(fs, nargs, 0);
check_table(vm, tv, "sort")?;
let comp = match vm.nat_arg(fs, nargs, 1) {
Value::Nil => None,
f @ (Value::Closure(_) | Value::Native(_)) => Some(f),
v => {
return Err(arg_error(
vm,
2,
"sort",
&format!("function expected, got {}", v.type_name()),
));
}
};
let n = vm.checked_len(tv)?;
if n > i64::from(u32::MAX) {
return Err(arg_error(vm, 1, "sort", "array too big"));
}
let mut v: Vec<Value> = Vec::with_capacity(n.max(0) as usize);
for i in 1..=n {
v.push(vm.index_value(tv, Value::Int(i))?);
}
sort_slice(vm, &mut v, comp)?;
for (i, &val) in v.iter().enumerate() {
vm.newindex_value(tv, Value::Int(i as i64 + 1), val)?;
}
Ok(0)
}
fn lt(vm: &mut Vm, comp: Option<Value>, a: Value, b: Value) -> Result<bool, LuaError> {
match comp {
Some(f) => Ok(vm
.call_noyield(f, &[a, b])?
.first()
.copied()
.unwrap_or(Value::Nil)
.truthy()),
None => vm.less_than(a, b, false),
}
}
fn sort_slice(vm: &mut Vm, v: &mut [Value], comp: Option<Value>) -> Result<(), LuaError> {
fn quick(
vm: &mut Vm,
v: &mut [Value],
comp: Option<Value>,
mut lo: usize,
mut hi: usize,
) -> Result<(), LuaError> {
while lo < hi {
if hi - lo < 3 {
for i in lo + 1..=hi {
let mut j = i;
while j > lo && lt(vm, comp, v[j], v[j - 1])? {
v.swap(j, j - 1);
j -= 1;
}
}
return Ok(());
}
let mid = lo + (hi - lo) / 2;
if lt(vm, comp, v[mid], v[lo])? {
v.swap(mid, lo);
}
if lt(vm, comp, v[hi], v[mid])? {
v.swap(hi, mid);
if lt(vm, comp, v[mid], v[lo])? {
v.swap(mid, lo);
}
}
let pivot = v[mid];
v.swap(mid, hi - 1);
let (mut i, mut j) = (lo, hi - 1);
loop {
i += 1;
while lt(vm, comp, v[i], pivot)? {
if i >= hi {
return Err(raise_str(vm, "invalid order function for sorting"));
}
i += 1;
}
j -= 1;
while lt(vm, comp, pivot, v[j])? {
if j <= lo {
return Err(raise_str(vm, "invalid order function for sorting"));
}
j -= 1;
}
if i >= j {
break;
}
v.swap(i, j);
}
v.swap(i, hi - 1);
if i - lo < hi - i {
if i > 0 {
quick(vm, v, comp, lo, i - 1)?;
}
lo = i + 1;
} else {
quick(vm, v, comp, i + 1, hi)?;
if i == 0 {
break;
}
hi = i - 1;
}
}
Ok(())
}
if !v.is_empty() {
let hi = v.len() - 1;
quick(vm, v, comp, 0, hi)?;
}
Ok(())
}