luallaby 0.1.0

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
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,
    // Lowest key (possibly) available in map
    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() {
            // If table was always sequence, shortcut
            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 {
            // i <= min_map <=> (i - 1) < min_map
            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; // i > 0, so not overflowing
                self.vec.get(i)
            }
            key => self.map.get(key),
        }
        .cloned()
        .unwrap_or(Value::Nil)
    }

    // Whenever adding an item to the table, we always want to ensure that at least half of the
    // slots in the Vec part of the table are filled. This ensures we don't run into memory issues
    // when we have a sparsely populated Vec part of the table.
    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) {
            // Insert into next_key map
            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; // i > 0, so not overflowing
                if i < Self::MIN_VEC_SLOTS || (self.min_map > 0 && i < self.min_map) {
                    // No need to consider resizing, just add to vec part
                    self.set_vec(i, value);
                } else if (self.vec_len + 1) * 2 >= i {
                    if self.min_map > 0 {
                        // At least half of the slots would be filled if we add item at index i,
                        // redistrubute keys between map and vec, and to prevent having to do that
                        // again the next nearby key, take out a bit more
                        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(())
    }

    // Take integer keys up to and including index out of map into vec
    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 {
            // replaced non-nil, meaning slot was emptied
            self.vec_len -= 1;
        } else if !removed && old == Value::Nil {
            // nil replaced, meaning empty slot filled
            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);
        }
    }
}