use std::{cell::RefCell, collections::HashMap, rc::Rc};
use crate::{Error, LuaError, Result, Value};
use super::{LuaInt, Numeric};
#[derive(Clone, Debug, Default)]
pub struct Table {
vec: Vec<Value>,
vec_len: usize,
min_map: usize,
map: HashMap<Value, Value>,
keys: HashMap<Value, Value>,
meta: Option<Rc<RefCell<Table>>>,
}
impl Table {
const MIN_VEC_SLOTS: usize = 1_000;
const MAX_VEC: i64 = i32::MAX as i64;
pub(crate) fn clear(&mut self) {
self.keys.clear();
self.map.clear();
self.min_map = 0;
self.vec.clear();
self.vec_len = 0;
self.meta.take();
}
pub(crate) fn get_meta(&self) -> &Option<Rc<RefCell<Table>>> {
&self.meta
}
pub(crate) fn set_meta(&mut self, meta: Option<Rc<RefCell<Table>>>) {
self.meta = meta;
}
pub(crate) fn next_key(&self, index: Value) -> Result<Value> {
if self.keys.is_empty() {
return Ok(Value::Nil);
}
let mut next = self
.keys
.get(&index)
.cloned()
.ok_or_else(|| Error::from_lua(LuaError::NextInvalidKey))?;
while next != Value::Nil && self.get(&next) == Value::Nil {
next = self.keys.get(&next).cloned().unwrap_or(Value::Nil);
}
Ok(next)
}
pub(crate) fn len(&self) -> usize {
self.vec_len + self.map.len()
}
pub(crate) fn border(&self) -> usize {
if self.vec_len == Self::MAX_VEC as usize {
let mut border = Self::MAX_VEC as usize;
while border <= LuaInt::MAX as usize
&& self.map.get(&Value::int(border as i64)).is_some()
{
border += 1;
}
border - 1
} else if self.vec_len == self.vec.len() {
self.vec_len
} else {
self.vec
.iter()
.position(|v| v == &Value::Nil)
.unwrap_or(self.vec.len())
}
}
pub(crate) fn get(&self, key: &Value) -> Value {
match key {
Value::Number(n) => self._get(&Value::Number(n.coerce_int_failover())),
v => self._get(v),
}
}
#[inline]
fn _get(&self, key: &Value) -> Value {
match key {
Value::Number(Numeric::Integer(i))
if *i > 0
&& (*i <= Self::MIN_VEC_SLOTS as i64
|| (self.min_map > 0 && *i <= self.min_map as i64)
|| (self.min_map == 0 && *i <= Self::MAX_VEC)) =>
{
let i = *i as usize - 1; self.vec.get(i)
}
key => self.map.get(key),
}
.cloned()
.unwrap_or(Value::Nil)
}
pub(crate) fn set(&mut self, key: Value, value: Value) -> Result<()> {
if matches!(key, Value::Nil)
|| matches!(key, Value::Number(Numeric::Float(f)) if f.is_nan())
{
return err!(LuaError::TableIndex);
}
let key = match key {
Value::Number(n) => Value::Number(n.coerce_int_failover()),
v => v,
};
if value != Value::Nil && !self.keys.contains_key(&key) {
let prev = self.keys.insert(Value::Nil, key.clone());
self.keys.insert(key.clone(), prev.unwrap_or(Value::Nil));
}
match key {
Value::Number(Numeric::Integer(i)) if i > 0 && i <= Self::MAX_VEC => {
let i = i as usize - 1; if i < Self::MIN_VEC_SLOTS || (self.min_map > 0 && i < self.min_map) {
self.set_vec(i, value);
} else if (self.vec_len + 1) * 2 >= i {
if self.min_map > 0 {
self.redistribute(i * 2);
}
self.set_vec(i, value);
} else {
self.min_map = if self.min_map == 0 {
i
} else {
self.min_map.min(i)
};
self.set_map(key, value);
}
}
key => self.set_map(key, value),
}
Ok(())
}
fn redistribute(&mut self, idx: usize) {
self.min_map = idx + 1;
let mut removed = Vec::new();
for key in self.map.keys() {
if let Value::Number(Numeric::Integer(n)) = key {
if *n > 0 && *n <= idx as i64 && *n <= Self::MAX_VEC {
let idx = *n as usize - 1;
removed.push((key.clone(), idx));
}
}
}
for (key, idx) in removed.into_iter() {
let value = self.map.remove(&key).unwrap();
self.set_vec(idx, value);
}
}
fn set_vec(&mut self, idx: usize, value: Value) {
if idx >= self.vec.len() {
self.vec.resize(idx + 1, Value::Nil);
}
let removed = value == Value::Nil;
let old = std::mem::replace(&mut self.vec[idx], value);
if removed && old != Value::Nil {
self.vec_len -= 1;
} else if !removed && old == Value::Nil {
self.vec_len += 1;
}
}
fn set_map(&mut self, key: Value, value: Value) {
if let Value::Nil = value {
self.map.remove(&key);
} else {
self.map.insert(key, value);
}
}
}