kittycad_execution_plan/
memory.rs

1use std::cmp::Ordering;
2
3use kittycad_execution_plan_traits::{
4    events::{Event, EventWriter},
5    InMemory, ListHeader, MemoryError, NumericPrimitive, ObjectHeader, Primitive, ReadMemory, Value,
6};
7
8use crate::{Address, ExecutionError};
9
10/// Helper wrapper around Memory. It lets you push static data into memory before the program runs.
11pub struct StaticMemoryInitializer {
12    memory: Memory,
13    last: Address,
14}
15
16impl Default for StaticMemoryInitializer {
17    fn default() -> Self {
18        Self {
19            memory: Default::default(),
20            last: Address::ZERO,
21        }
22    }
23}
24
25impl StaticMemoryInitializer {
26    /// Finish putting static data into memory, get ready to execute the plan.
27    /// Returns normal execution plan program memory.
28    pub fn finish(self) -> Memory {
29        self.memory
30    }
31
32    /// Put the next value into memory.
33    /// Returns the address that the value was inserted at.
34    pub fn push<T: Value>(&mut self, val: T) -> Address {
35        let addr_of_value = self.last;
36        let len = self.memory.set_composite(self.last, val);
37        self.last = self.last.offset(len);
38        addr_of_value
39    }
40}
41
42/// KCEP's program memory. A flat, linear list of values.
43#[derive(Debug, Clone)]
44#[cfg_attr(test, derive(PartialEq))]
45pub struct Memory {
46    /// Each address of memory.
47    pub addresses: Vec<Option<Primitive>>,
48    /// A stack where temporary values can be pushed or popped.
49    pub stack: Stack<Vec<Primitive>>,
50    /// Special storage for SketchGroups.
51    pub sketch_groups: Vec<crate::sketch_types::SketchGroup>,
52}
53
54impl Default for Memory {
55    fn default() -> Self {
56        Self {
57            addresses: vec![None; 1024],
58            stack: Stack::default(),
59            sketch_groups: Vec::new(),
60        }
61    }
62}
63
64impl kittycad_execution_plan_traits::ReadMemory for Memory {
65    /// Get a value from KCEP's program memory.
66    fn get(&self, address: &Address) -> Option<&Primitive> {
67        self.addresses[inner(*address)].as_ref()
68    }
69
70    /// Get a value value (i.e. a value which takes up multiple addresses in memory).
71    /// Its parts are stored in consecutive memory addresses starting at `start`.
72    fn get_composite<T: Value>(&self, start: Address) -> Result<(T, usize), MemoryError> {
73        let mut values = self.addresses.iter().skip(inner(start)).cloned();
74        T::from_parts(&mut values)
75    }
76
77    fn stack_pop(&mut self) -> Result<Vec<Primitive>, MemoryError> {
78        self.stack.pop()
79    }
80
81    fn stack_peek(&self) -> std::result::Result<Vec<Primitive>, MemoryError> {
82        self.stack.peek().cloned()
83    }
84}
85
86impl Memory {
87    /// Store a value in KCEP's program memory.
88    pub fn set(&mut self, address: Address, value: Primitive) {
89        let addr = address - Address::ZERO;
90        // If isn't big enough for this value, double the size of memory until it is.
91        while addr > self.addresses.len() {
92            self.addresses.extend(vec![None; self.addresses.len()]);
93        }
94        self.addresses[addr] = Some(value);
95    }
96
97    /// Store a value value (i.e. a value which takes up multiple addresses in memory).
98    /// Store its parts in consecutive memory addresses starting at `start`.
99    /// Returns how many memory addresses the data took up.
100    pub fn set_composite<T: Value>(&mut self, start: Address, composite_value: T) -> usize {
101        let parts = composite_value.into_parts().into_iter();
102
103        let mut total_addrs = 0;
104        for (value, addr) in parts.zip(start - Address::ZERO..) {
105            self.addresses[addr] = Some(value);
106            total_addrs += 1;
107        }
108        total_addrs
109    }
110
111    /// Iterate over each memory address and its value.
112    pub fn iter(&self) -> impl Iterator<Item = (usize, &Option<Primitive>)> {
113        self.addresses.iter().enumerate()
114    }
115
116    /// Get a primitive from `addr`. If it's of type T, extract that T. Otherwise error.
117    pub fn get_primitive<T>(&self, addr: &Address) -> Result<T, ExecutionError>
118    where
119        T: TryFrom<Primitive, Error = MemoryError>,
120    {
121        let primitive = self
122            .get(addr)
123            .cloned()
124            .ok_or(ExecutionError::MemoryEmpty { addr: *addr })?;
125        primitive.try_into().map_err(ExecutionError::MemoryError)
126    }
127
128    /// Look for either a usize or an object/list header at the given address.
129    /// Return that usize, or the `size` field of the header.
130    pub fn get_size(&self, addr: &Address) -> Result<usize, ExecutionError> {
131        let primitive = self.get(addr).ok_or(ExecutionError::MemoryEmpty { addr: *addr })?;
132        let size = match primitive {
133            Primitive::NumericValue(NumericPrimitive::UInteger(size)) => *size,
134            Primitive::ListHeader(ListHeader { count: _, size }) => *size,
135            Primitive::ObjectHeader(ObjectHeader { properties: _, size }) => *size,
136            other => {
137                return Err(ExecutionError::MemoryError(MemoryError::MemoryWrongType {
138                    expected: "ObjectHeader, ListHeader, or usize",
139                    actual: format!("{other:?}"),
140                }))
141            }
142        };
143        Ok(size)
144    }
145
146    /// Get a range of addresses, starting at `start` and continuing for `len` more.
147    pub fn get_slice(&self, start: Address, len: usize) -> Result<Vec<Primitive>, ExecutionError> {
148        let slice = &self.addresses[inner(start)..inner(start) + len];
149        let x = slice
150            .iter()
151            .cloned()
152            .enumerate()
153            .map(|(addr, prim)| {
154                prim.ok_or(ExecutionError::MemoryEmpty {
155                    addr: Address::ZERO + addr,
156                })
157            })
158            .collect::<Result<Vec<_>, _>>()?;
159        Ok(x)
160    }
161
162    /// Read a T value out of memory (either addressable or stack).
163    pub fn get_in_memory<T: Value>(
164        &mut self,
165        source: InMemory,
166        field_name: &'static str,
167        events: &mut EventWriter,
168    ) -> Result<(T, usize), MemoryError> {
169        match source {
170            InMemory::Address(a) => {
171                events.push(Event {
172                    text: format!("reading '{field_name}'"),
173                    severity: kittycad_execution_plan_traits::events::Severity::Debug,
174                    related_addresses: vec![a],
175                });
176                self.get_composite(a)
177            }
178            InMemory::StackPop => {
179                events.push(Event {
180                    text: format!("popping '{field_name}' from stack"),
181                    severity: kittycad_execution_plan_traits::events::Severity::Debug,
182                    related_addresses: Default::default(),
183                });
184                let data = self.stack_pop()?;
185                let mut data_parts = data.iter().cloned().map(Some);
186                T::from_parts(&mut data_parts)
187            }
188            InMemory::StackPeek => {
189                events.push(Event {
190                    text: format!("peeking '{field_name}' from stack"),
191                    severity: kittycad_execution_plan_traits::events::Severity::Debug,
192                    related_addresses: Default::default(),
193                });
194                let data = self.stack_peek()?;
195                let mut data_parts = data.iter().cloned().map(Some);
196                T::from_parts(&mut data_parts)
197            }
198        }
199    }
200
201    /// Return a nicely-formatted table of stack.
202    #[must_use]
203    pub fn debug_table_stack(&self) -> String {
204        #[derive(tabled::Tabled)]
205        struct StackLevel {
206            depth: usize,
207            value: String,
208        }
209
210        let table_data: Vec<_> = self
211            .stack
212            .inner
213            .iter()
214            .enumerate()
215            .map(|(depth, slice)| StackLevel {
216                depth,
217                value: format!("{slice:?}"),
218            })
219            .collect();
220        tabled::Table::new(table_data)
221            .with(tabled::settings::Style::sharp())
222            .to_string()
223    }
224
225    /// Return a nicely-formatted table of memory.
226    #[must_use]
227    pub fn debug_table(&self, up_to: Option<usize>) -> String {
228        #[derive(tabled::Tabled)]
229        struct MemoryAddr {
230            addr: String,
231            val_type: &'static str,
232            value: String,
233        }
234        let table_data: Vec<_> = self
235            .iter()
236            .filter_map(|(i, val)| {
237                if let Some(val) = val {
238                    let (val_type, value) = pretty_print(val);
239                    Some(MemoryAddr {
240                        addr: i.to_string(),
241                        val_type,
242                        value,
243                    })
244                } else if let Some(up_to) = up_to {
245                    if i <= up_to {
246                        Some(MemoryAddr {
247                            addr: i.to_string(),
248                            val_type: "",
249                            value: "".to_owned(),
250                        })
251                    } else {
252                        None
253                    }
254                } else {
255                    None
256                }
257            })
258            .collect();
259        tabled::Table::new(table_data)
260            .with(tabled::settings::Style::sharp())
261            .to_string()
262    }
263
264    /// Get the address of the last non-empty address.
265    /// If none, then all addresses are empty.
266    #[must_use]
267    pub fn last_nonempty_address(&self) -> Option<usize> {
268        self.iter()
269            .filter_map(|(i, v)| if v.is_some() { Some(i) } else { None })
270            .last()
271    }
272
273    /// Get the address of the last empty cell.
274    /// If none, then all cells are occupied.
275    pub fn next_empty_cell(&self) -> Option<usize> {
276        self.iter().position(|(_, v)| v.is_none())
277    }
278
279    pub(crate) fn sketch_group_set(
280        &mut self,
281        sketch_group: crate::sketch_types::SketchGroup,
282        destination: usize,
283    ) -> Result<(), ExecutionError> {
284        match destination.cmp(&self.sketch_groups.len()) {
285            Ordering::Less => {
286                self.sketch_groups[destination] = sketch_group;
287            }
288            Ordering::Equal => {
289                self.sketch_groups.push(sketch_group);
290            }
291            Ordering::Greater => {
292                return Err(ExecutionError::SketchGroupNoGaps {
293                    destination,
294                    len: self.sketch_groups.len(),
295                });
296            }
297        }
298        Ok(())
299    }
300}
301
302fn pretty_print(p: &Primitive) -> (&'static str, String) {
303    match p {
304        Primitive::String(v) => ("String", v.to_owned()),
305        Primitive::NumericValue(NumericPrimitive::Float(v)) => ("Float", v.to_string()),
306        Primitive::NumericValue(NumericPrimitive::UInteger(v)) => ("Uint", v.to_string()),
307        Primitive::NumericValue(NumericPrimitive::Integer(v)) => ("Int", v.to_string()),
308        Primitive::Uuid(v) => ("Uuid", v.to_string()),
309        Primitive::Bytes(_) => ("Bytes", String::new()),
310        Primitive::Bool(v) => ("Bool", v.to_string()),
311        Primitive::ListHeader(ListHeader { count, size }) => {
312            ("List header", format!("{count} elements, {size} primitives"))
313        }
314        Primitive::ObjectHeader(ObjectHeader { properties, size }) => (
315            "Object header",
316            format!("keys {}, {size} primitives", properties.clone().join(",")),
317        ),
318        Primitive::Nil => ("Nil", String::new()),
319        Primitive::Address(a) => ("Address", a.to_string()),
320    }
321}
322
323fn inner(a: Address) -> usize {
324    a - Address::ZERO
325}
326
327/// A stack where values can be pushed/popped.
328#[derive(Debug, Eq, PartialEq, Default, Clone)]
329pub struct Stack<T> {
330    inner: Vec<T>,
331}
332
333impl<T> Stack<T> {
334    /// Add a value to the top of the stack (above any previous values).
335    pub fn push(&mut self, t: T) {
336        self.inner.push(t);
337    }
338    /// Remove a value from the top of the stack, and return it.
339    pub fn pop(&mut self) -> Result<T, MemoryError> {
340        self.inner.pop().ok_or(MemoryError::StackEmpty)
341    }
342    /// Return the value from the top of the stack.
343    pub fn peek(&self) -> Result<&T, MemoryError> {
344        self.inner.last().ok_or(MemoryError::StackEmpty)
345    }
346    /// Is the stack empty?
347    pub fn is_empty(&self) -> bool {
348        self.inner.is_empty()
349    }
350    /// Iterate over the stack, from top to bottom.
351    pub fn iter(&self) -> impl Iterator<Item = &T> {
352        self.inner.iter().rev()
353    }
354    /// How many items are currently in the stack?
355    pub fn len(&self) -> usize {
356        self.inner.len()
357    }
358}
359
360impl<T> Stack<Vec<T>> {
361    /// Extend the top of stack with more data.
362    pub fn extend(&mut self, data: Vec<T>) -> Result<(), MemoryError> {
363        let mut start = self.pop()?;
364        start.extend(data);
365        self.push(start);
366        Ok(())
367    }
368}
369
370impl Stack<Vec<Primitive>> {
371    /// Remove a value from the top of the stack, and return it.
372    /// If it's a single primitive long, return Ok, otherwise error.
373    pub fn pop_single(&mut self) -> Result<Primitive, ExecutionError> {
374        let mut slice = self.pop()?;
375        let prim = slice.pop().ok_or(MemoryError::StackNotPrimitive { actual_length: 0 })?;
376        if !slice.is_empty() {
377            return Err(ExecutionError::MemoryError(MemoryError::StackNotPrimitive {
378                actual_length: slice.len() + 1,
379            }));
380        }
381        Ok(prim)
382    }
383}