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};
|
use crate::{ExecutionError, Value};
|
||||||
|
|
||||||
|
mod impls;
|
||||||
|
|
||||||
/// Types that can be written to or read from KCEP program memory,
|
/// Types that can be written to or read from KCEP program memory,
|
||||||
/// but require multiple values to store.
|
/// but require multiple values to store.
|
||||||
/// They get laid out into multiple consecutive memory addresses.
|
/// They get laid out into multiple consecutive memory addresses.
|
||||||
pub trait Composite: Sized {
|
pub trait Composite: Sized {
|
||||||
/// How many memory addresses are required to store this value?
|
|
||||||
const SIZE: usize;
|
|
||||||
/// Store the value in memory.
|
/// Store the value in memory.
|
||||||
fn into_parts(self) -> Vec<Value>;
|
fn into_parts(self) -> Vec<Value>;
|
||||||
/// Read the value from memory.
|
/// Read the value from memory.
|
||||||
fn from_parts(values: Vec<Value>) -> Result<Self, ExecutionError>;
|
fn from_parts(values: &[Option<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 })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
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 composite::Composite;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashMap, fmt};
|
use std::fmt;
|
||||||
|
|
||||||
mod composite;
|
mod composite;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
/// KCEP's program memory. A flat, linear list of values.
|
/// KCEP's program memory. A flat, linear list of values.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[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.
|
/// An address in KCEP's program memory.
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
@ -37,13 +43,17 @@ impl From<usize> for Address {
|
|||||||
|
|
||||||
impl Memory {
|
impl Memory {
|
||||||
/// Get a value from KCEP's program memory.
|
/// Get a value from KCEP's program memory.
|
||||||
pub fn get(&self, addr: &Address) -> Option<&Value> {
|
pub fn get(&self, Address(addr): &Address) -> Option<&Value> {
|
||||||
self.0.get(&addr.0)
|
self.0[*addr].as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a value in KCEP's program memory.
|
/// Store a value in KCEP's program memory.
|
||||||
pub fn set(&mut self, addr: Address, value: Value) {
|
pub fn set(&mut self, Address(addr): Address, value: Value) {
|
||||||
self.0.insert(addr.0, 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).
|
/// 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) {
|
pub fn set_composite<T: Composite>(&mut self, composite_value: T, start: Address) {
|
||||||
let parts = composite_value.into_parts().into_iter();
|
let parts = composite_value.into_parts().into_iter();
|
||||||
for (value, addr) in parts.zip(start.0..) {
|
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).
|
/// 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`.
|
/// Its parts are stored in consecutive memory addresses starting at `start`.
|
||||||
pub fn get_composite<T: Composite>(&self, start: Address) -> Result<T, ExecutionError> {
|
pub fn get_composite<T: Composite>(&self, start: Address) -> Result<T, ExecutionError> {
|
||||||
let addrs = start.0..start.0 + T::SIZE;
|
let values = &self.0[start.0..];
|
||||||
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<_, _>>()?;
|
|
||||||
T::from_parts(values)
|
T::from_parts(values)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,7 +205,7 @@ impl Arithmetic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Operations that can be applied to values in memory.
|
/// Operations that can be applied to values in memory.
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Copy)]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
Add,
|
Add,
|
||||||
Mul,
|
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.
|
/// Errors that could occur when executing a KittyCAD execution plan.
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error, Clone)]
|
||||||
pub enum ExecutionError {
|
pub enum ExecutionError {
|
||||||
#[error("Memory address {addr} was not set")]
|
#[error("Memory address {addr} was not set")]
|
||||||
MemoryEmpty { addr: Address },
|
MemoryEmpty { addr: Address },
|
||||||
@ -273,6 +274,6 @@ pub enum ExecutionError {
|
|||||||
CannotApplyOperation { op: Operation, operands: Vec<Value> },
|
CannotApplyOperation { op: Operation, operands: Vec<Value> },
|
||||||
#[error("Tried to read a '{expected}' from KCEP program memory, found an '{actual}' instead")]
|
#[error("Tried to read a '{expected}' from KCEP program memory, found an '{actual}' instead")]
|
||||||
MemoryWrongType { expected: &'static str, actual: String },
|
MemoryWrongType { expected: &'static str, actual: String },
|
||||||
#[error("Wrong size of memory trying to read value from KCEP program memory: got {actual} but wanted {expected}")]
|
#[error("Wanted {expected} values but did not get enough")]
|
||||||
MemoryWrongSize { expected: usize, actual: usize },
|
MemoryWrongSize { expected: usize },
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,14 +58,11 @@ fn add_to_composite_value() {
|
|||||||
|
|
||||||
// Write a point to memory.
|
// Write a point to memory.
|
||||||
let point_before = Point3D { x: 2.0, y: 3.0, z: 4.0 };
|
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);
|
mem.set_composite(point_before, start_addr);
|
||||||
assert_eq!(
|
assert_eq!(mem.0[0], Some(2.0.into()));
|
||||||
mem,
|
assert_eq!(mem.0[1], Some(3.0.into()));
|
||||||
Memory(HashMap::from(
|
assert_eq!(mem.0[2], Some(4.0.into()));
|
||||||
[(100, 2.0.into()), (101, 3.0.into()), (102, 4.0.into()),]
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update the point's x-value in memory.
|
// Update the point's x-value in memory.
|
||||||
execute(
|
execute(
|
||||||
|
|||||||
Reference in New Issue
Block a user