Skip to main content

jeff/reader/
op.rs

1//! Node operation definitions.
2
3use crate::reader::value::{ValueTable, WireValue};
4use crate::types::Type;
5use crate::{jeff_capnp, Direction};
6
7use super::metadata::sealed::HasMetadataSealed;
8use super::optype::OpType;
9use super::string_table::StringTable;
10use super::value::ValueId;
11use super::ReadError;
12
13/// Operation in a dataflow graph.
14#[derive(Clone, Copy, Debug)]
15pub struct Operation<'a> {
16    /// Internal capnproto region definition.
17    op: jeff_capnp::op::Reader<'a>,
18    /// Module-level register of reused strings.
19    strings: StringTable<'a>,
20    /// Function-level register of typed hyperedges.
21    values: ValueTable<'a>,
22}
23
24impl<'a> Operation<'a> {
25    /// Create a new dataflow operation reader from a capnp reader.
26    pub(crate) fn read_capnp(
27        operation: jeff_capnp::op::Reader<'a>,
28        strings: StringTable<'a>,
29        values: ValueTable<'a>,
30    ) -> Self {
31        Self {
32            op: operation,
33            strings,
34            values,
35        }
36    }
37
38    /// Returns the type of this operation.
39    pub fn op_type(&self) -> OpType<'a> {
40        OpType::read_capnp(self.op.get_instruction(), self.strings, self.values)
41    }
42
43    /// Returns an iterator over the input or output values of this operation.
44    ///
45    /// # Errors
46    ///
47    /// - [`ReadError::ValueOutOfBounds`] if an encoded value references an invalid index in the value table.
48    pub fn boundary(
49        &self,
50        direction: Direction,
51    ) -> impl Iterator<Item = Result<WireValue<'a>, ReadError>> {
52        let value_table = self.values;
53        let values = match direction {
54            Direction::Incoming => self.op.get_inputs(),
55            Direction::Outgoing => self.op.get_outputs(),
56        }
57        .expect("Boundary should be present");
58        values.iter().map(move |idx| value_table.get(idx))
59    }
60
61    /// Return an iterator over the input values of this operation.
62    ///
63    /// # Errors
64    ///
65    /// - [`ReadError::ValueOutOfBounds`] if an encoded value references an invalid index in the value table.
66    pub fn inputs(&self) -> impl Iterator<Item = Result<WireValue<'a>, ReadError>> {
67        self.boundary(Direction::Incoming)
68    }
69
70    /// Return an iterator over the output values of this operation.
71    ///
72    /// # Errors
73    ///
74    /// - [`ReadError::ValueOutOfBounds`] if an encoded value references an invalid index in the value table.
75    pub fn outputs(&self) -> impl Iterator<Item = Result<WireValue<'a>, ReadError>> {
76        self.boundary(Direction::Outgoing)
77    }
78
79    /// Returns the number of inputs or output values in this operation.
80    pub fn boundary_count(&self, direction: Direction) -> usize {
81        match direction {
82            Direction::Incoming => self.op.get_inputs(),
83            Direction::Outgoing => self.op.get_outputs(),
84        }
85        .expect("Boundary should be present")
86        .len() as usize
87    }
88
89    /// Returns the number of input values in this operation.
90    pub fn input_count(&self) -> usize {
91        self.boundary_count(Direction::Incoming)
92    }
93
94    /// Returns the number of output values in this operation.
95    pub fn output_count(&self) -> usize {
96        self.boundary_count(Direction::Outgoing)
97    }
98
99    /// Returns the boundary value at the given index, or `None` if the index is
100    /// out of bounds.
101    ///
102    /// # Errors
103    ///
104    /// - [`ReadError::ValueOutOfBounds`] if the encoded value references an invalid index in the value table.
105    pub fn boundary_value(
106        &self,
107        direction: Direction,
108        idx: usize,
109    ) -> Option<Result<WireValue<'a>, ReadError>> {
110        let values = match direction {
111            Direction::Incoming => self.op.get_inputs(),
112            Direction::Outgoing => self.op.get_outputs(),
113        }
114        .expect("Boundary should be present");
115        if idx >= values.len() as usize {
116            return None;
117        }
118        let value_id: ValueId = values.get(idx as u32);
119        Some(self.values.get(value_id))
120    }
121
122    /// Returns the input value at the given index, or `None` if the index is
123    /// out of bounds.
124    ///
125    /// # Errors
126    ///
127    /// - [`ReadError::ValueOutOfBounds`] if the encoded value references an invalid index in the value table.
128    pub fn input(&self, idx: usize) -> Option<Result<WireValue<'a>, ReadError>> {
129        self.boundary_value(Direction::Incoming, idx)
130    }
131
132    /// Returns the output value at the given index, or `None` if the index is
133    /// out of bounds.
134    ///
135    /// # Errors
136    ///
137    /// - [`ReadError::ValueOutOfBounds`] if the encoded value references an invalid index in the value table.
138    pub fn output(&self, idx: usize) -> Option<Result<WireValue<'a>, ReadError>> {
139        self.boundary_value(Direction::Outgoing, idx)
140    }
141
142    /// Returns the input types of this function.
143    pub fn input_types(&self) -> impl Iterator<Item = Result<Type, ReadError>> + 'a {
144        self.inputs().map(move |res| res.map(|t| t.ty()))
145    }
146
147    /// Returns the output types of this function.
148    pub fn output_types(&self) -> impl Iterator<Item = Result<Type, ReadError>> + 'a {
149        self.outputs().map(move |res| res.map(|t| t.ty()))
150    }
151}
152
153impl<'a> HasMetadataSealed for Operation<'a> {
154    fn strings(&self) -> StringTable<'a> {
155        self.strings
156    }
157
158    fn metadata_reader(&self) -> capnp::struct_list::Reader<'a, jeff_capnp::meta::Owned> {
159        self.op.get_metadata().expect("Metadata should be present")
160    }
161}