Execution plans should allow dynamic sized types (#1178)
* Execution plans should allow dynamic sized types * move Composite impls into their own file
This commit is contained in:
@ -1,39 +1,13 @@
|
||||
use crate::{ExecutionError, Value};
|
||||
|
||||
mod impls;
|
||||
|
||||
/// Types that can be written to or read from KCEP program memory,
|
||||
/// but require multiple values to store.
|
||||
/// They get laid out into multiple consecutive memory addresses.
|
||||
pub trait Composite: Sized {
|
||||
/// How many memory addresses are required to store this value?
|
||||
const SIZE: usize;
|
||||
/// Store the value in memory.
|
||||
fn into_parts(self) -> Vec<Value>;
|
||||
/// Read the value from memory.
|
||||
fn from_parts(values: Vec<Value>) -> Result<Self, ExecutionError>;
|
||||
}
|
||||
|
||||
impl Composite for kittycad::types::Point3D {
|
||||
fn into_parts(self) -> Vec<Value> {
|
||||
let points = [self.x, self.y, self.z];
|
||||
points
|
||||
.into_iter()
|
||||
.map(|x| Value::NumericValue(crate::NumericValue::Float(x)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
const SIZE: usize = 3;
|
||||
|
||||
fn from_parts(values: Vec<Value>) -> Result<Self, ExecutionError> {
|
||||
let n = values.len();
|
||||
let Ok([x, y, z]): Result<[Value; 3], _> = values.try_into() else {
|
||||
return Err(ExecutionError::MemoryWrongSize {
|
||||
actual: n,
|
||||
expected: Self::SIZE,
|
||||
});
|
||||
};
|
||||
let x = x.try_into()?;
|
||||
let y = y.try_into()?;
|
||||
let z = z.try_into()?;
|
||||
Ok(Self { x, y, z })
|
||||
}
|
||||
fn from_parts(values: &[Option<Value>]) -> Result<Self, ExecutionError>;
|
||||
}
|
||||
|
||||
22
src/wasm-lib/execution-plan/src/composite/impls.rs
Normal file
22
src/wasm-lib/execution-plan/src/composite/impls.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use crate::{ExecutionError, Value};
|
||||
|
||||
use super::Composite;
|
||||
|
||||
impl Composite for kittycad::types::Point3D {
|
||||
fn into_parts(self) -> Vec<Value> {
|
||||
let points = [self.x, self.y, self.z];
|
||||
points
|
||||
.into_iter()
|
||||
.map(|x| Value::NumericValue(crate::NumericValue::Float(x)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn from_parts(values: &[Option<Value>]) -> Result<Self, ExecutionError> {
|
||||
let err = ExecutionError::MemoryWrongSize { expected: 3 };
|
||||
let [x, y, z] = [0, 1, 2].map(|n| values.get(n).ok_or(err.clone()));
|
||||
let x = x?.to_owned().ok_or(err.clone())?.try_into()?;
|
||||
let y = y?.to_owned().ok_or(err.clone())?.try_into()?;
|
||||
let z = z?.to_owned().ok_or(err.clone())?.try_into()?;
|
||||
Ok(Self { x, y, z })
|
||||
}
|
||||
}
|
||||
@ -8,16 +8,22 @@
|
||||
|
||||
use composite::Composite;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, fmt};
|
||||
use std::fmt;
|
||||
|
||||
mod composite;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// KCEP's program memory. A flat, linear list of values.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct Memory(HashMap<usize, Value>);
|
||||
pub struct Memory(Vec<Option<Value>>);
|
||||
|
||||
impl Default for Memory {
|
||||
fn default() -> Self {
|
||||
Self(vec![None; 1024])
|
||||
}
|
||||
}
|
||||
|
||||
/// An address in KCEP's program memory.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
@ -37,13 +43,17 @@ impl From<usize> for Address {
|
||||
|
||||
impl Memory {
|
||||
/// Get a value from KCEP's program memory.
|
||||
pub fn get(&self, addr: &Address) -> Option<&Value> {
|
||||
self.0.get(&addr.0)
|
||||
pub fn get(&self, Address(addr): &Address) -> Option<&Value> {
|
||||
self.0[*addr].as_ref()
|
||||
}
|
||||
|
||||
/// Store a value in KCEP's program memory.
|
||||
pub fn set(&mut self, addr: Address, value: Value) {
|
||||
self.0.insert(addr.0, value);
|
||||
pub fn set(&mut self, Address(addr): Address, value: Value) {
|
||||
// If isn't big enough for this value, double the size of memory until it is.
|
||||
while addr > self.0.len() {
|
||||
self.0.extend(vec![None; self.0.len()]);
|
||||
}
|
||||
self.0[addr] = Some(value);
|
||||
}
|
||||
|
||||
/// Store a composite value (i.e. a value which takes up multiple addresses in memory).
|
||||
@ -51,23 +61,14 @@ impl Memory {
|
||||
pub fn set_composite<T: Composite>(&mut self, composite_value: T, start: Address) {
|
||||
let parts = composite_value.into_parts().into_iter();
|
||||
for (value, addr) in parts.zip(start.0..) {
|
||||
self.0.insert(addr, value);
|
||||
self.0[addr] = Some(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a composite value (i.e. a value which takes up multiple addresses in memory).
|
||||
/// Its parts are stored in consecutive memory addresses starting at `start`.
|
||||
pub fn get_composite<T: Composite>(&self, start: Address) -> Result<T, ExecutionError> {
|
||||
let addrs = start.0..start.0 + T::SIZE;
|
||||
let values: Vec<Value> = addrs
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
let addr = Address(a);
|
||||
self.get(&addr)
|
||||
.map(|x| x.to_owned())
|
||||
.ok_or(ExecutionError::MemoryEmpty { addr })
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
let values = &self.0[start.0..];
|
||||
T::from_parts(values)
|
||||
}
|
||||
}
|
||||
@ -204,7 +205,7 @@ impl Arithmetic {
|
||||
}
|
||||
|
||||
/// Operations that can be applied to values in memory.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
|
||||
pub enum Operation {
|
||||
Add,
|
||||
Mul,
|
||||
@ -265,7 +266,7 @@ pub fn execute(mem: &mut Memory, plan: Vec<Instruction>) -> Result<(), Execution
|
||||
}
|
||||
|
||||
/// Errors that could occur when executing a KittyCAD execution plan.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[derive(Debug, thiserror::Error, Clone)]
|
||||
pub enum ExecutionError {
|
||||
#[error("Memory address {addr} was not set")]
|
||||
MemoryEmpty { addr: Address },
|
||||
@ -273,6 +274,6 @@ pub enum ExecutionError {
|
||||
CannotApplyOperation { op: Operation, operands: Vec<Value> },
|
||||
#[error("Tried to read a '{expected}' from KCEP program memory, found an '{actual}' instead")]
|
||||
MemoryWrongType { expected: &'static str, actual: String },
|
||||
#[error("Wrong size of memory trying to read value from KCEP program memory: got {actual} but wanted {expected}")]
|
||||
MemoryWrongSize { expected: usize, actual: usize },
|
||||
#[error("Wanted {expected} values but did not get enough")]
|
||||
MemoryWrongSize { expected: usize },
|
||||
}
|
||||
|
||||
@ -58,14 +58,11 @@ fn add_to_composite_value() {
|
||||
|
||||
// Write a point to memory.
|
||||
let point_before = Point3D { x: 2.0, y: 3.0, z: 4.0 };
|
||||
let start_addr = Address(100);
|
||||
let start_addr = Address(0);
|
||||
mem.set_composite(point_before, start_addr);
|
||||
assert_eq!(
|
||||
mem,
|
||||
Memory(HashMap::from(
|
||||
[(100, 2.0.into()), (101, 3.0.into()), (102, 4.0.into()),]
|
||||
))
|
||||
);
|
||||
assert_eq!(mem.0[0], Some(2.0.into()));
|
||||
assert_eq!(mem.0[1], Some(3.0.into()));
|
||||
assert_eq!(mem.0[2], Some(4.0.into()));
|
||||
|
||||
// Update the point's x-value in memory.
|
||||
execute(
|
||||
|
||||
Reference in New Issue
Block a user