runmat-vm 0.6.0

RunMat virtual machine and bytecode interpreter
Documentation
use crate::indexing::plan::IndexPlan;
use crate::interpreter::errors::mex;
use runmat_builtins::{IntValue, IntegerStorage};
use runmat_runtime::RuntimeError;

#[derive(Clone)]
pub(crate) enum IntegerAssignmentValue {
    Exact(IntValue),
    Float(f64),
}

pub(crate) fn values(storage: &IntegerStorage) -> Vec<IntValue> {
    match storage {
        IntegerStorage::I8(values) => values.iter().copied().map(IntValue::I8).collect(),
        IntegerStorage::I16(values) => values.iter().copied().map(IntValue::I16).collect(),
        IntegerStorage::I32(values) => values.iter().copied().map(IntValue::I32).collect(),
        IntegerStorage::I64(values) => values.iter().copied().map(IntValue::I64).collect(),
        IntegerStorage::U8(values) => values.iter().copied().map(IntValue::U8).collect(),
        IntegerStorage::U16(values) => values.iter().copied().map(IntValue::U16).collect(),
        IntegerStorage::U32(values) => values.iter().copied().map(IntValue::U32).collect(),
        IntegerStorage::U64(values) => values.iter().copied().map(IntValue::U64).collect(),
    }
}

fn cast_signed(value: &IntegerAssignmentValue, min: i64, max: i64) -> i64 {
    match value {
        IntegerAssignmentValue::Exact(value) => value.to_i64().clamp(min, max),
        IntegerAssignmentValue::Float(value) if value.is_nan() => 0,
        IntegerAssignmentValue::Float(value) if value.is_infinite() => {
            if value.is_sign_negative() {
                min
            } else {
                max
            }
        }
        IntegerAssignmentValue::Float(value) => value.round().clamp(min as f64, max as f64) as i64,
    }
}

fn cast_unsigned(value: &IntegerAssignmentValue, max: u64) -> u64 {
    match value {
        IntegerAssignmentValue::Exact(IntValue::U64(value)) => (*value).min(max),
        IntegerAssignmentValue::Exact(value) => (value.to_i64().max(0) as u64).min(max),
        IntegerAssignmentValue::Float(value) if value.is_nan() || value.is_sign_negative() => 0,
        IntegerAssignmentValue::Float(value) if value.is_infinite() => max,
        IntegerAssignmentValue::Float(value) => value.round().clamp(0.0, max as f64) as u64,
    }
}

pub(crate) fn scatter(
    storage: &mut IntegerStorage,
    plan: &IndexPlan,
    rhs_values: &[IntegerAssignmentValue],
) -> Result<(), RuntimeError> {
    if rhs_values.len() != plan.indices.len() {
        return Err(mex("ShapeMismatch", "shape mismatch for slice assign"));
    }
    macro_rules! write_values {
        ($values:expr, $convert:expr) => {{
            for (&dst, value) in plan.indices.iter().zip(rhs_values.iter()) {
                $values[dst as usize] = $convert(value);
            }
        }};
    }
    match storage {
        IntegerStorage::I8(values) => write_values!(values, |value| cast_signed(
            value,
            i8::MIN as i64,
            i8::MAX as i64
        ) as i8),
        IntegerStorage::I16(values) => write_values!(values, |value| cast_signed(
            value,
            i16::MIN as i64,
            i16::MAX as i64
        ) as i16),
        IntegerStorage::I32(values) => write_values!(values, |value| cast_signed(
            value,
            i32::MIN as i64,
            i32::MAX as i64
        ) as i32),
        IntegerStorage::I64(values) => {
            write_values!(values, |value| cast_signed(value, i64::MIN, i64::MAX))
        }
        IntegerStorage::U8(values) => {
            write_values!(values, |value| cast_unsigned(value, u8::MAX as u64) as u8)
        }
        IntegerStorage::U16(values) => {
            write_values!(values, |value| cast_unsigned(value, u16::MAX as u64) as u16)
        }
        IntegerStorage::U32(values) => {
            write_values!(values, |value| cast_unsigned(value, u32::MAX as u64) as u32)
        }
        IntegerStorage::U64(values) => {
            write_values!(values, |value| cast_unsigned(value, u64::MAX))
        }
    }
    Ok(())
}