tis_100/
io.rs

1//! Constructs for passing messages between TIS-100 execution nodes.
2
3use vec_map::VecMap;
4use std::collections::HashMap;
5use std::collections::hash_map::Iter;
6use core::{Port, opposite_port};
7
8/// A unique identifier for a node.
9pub type NodeId = usize;
10
11/// A unique identifier for a port.
12pub type PortId = usize;
13
14/// A connection from one node to another through a port.
15#[derive(Debug)]
16pub struct Connection(PortId, NodeId);
17
18/// An `IoBus` is used to pass messages between nodes. Nodes are represented by `usize` indices.
19/// Nodes must first be connected before they can pass messages. Nodes can be connected using either
20/// half-duplex or full-duplex channels.
21///
22/// Once nodes are connected, they can pass messages through the IoBus using an `IoBusView`.
23/// An `IoBusView` ensures that a node can only read or write the ports that it is connected to.
24/// All writes are buffered. In order for a node to read a value that another node has sent it,
25/// the writes must be committed.
26///
27/// # Examples
28///
29/// ```
30/// use tis_100::core::Port::*;
31/// use tis_100::io::IoBus;
32///
33/// let mut bus = IoBus::new();
34///
35/// // In this example, 1 is right from 0, and 0 is left from 1.
36/// bus.connect_full(0, 1, RIGHT);
37/// assert!(bus.is_connected(0, 1, RIGHT));
38/// assert!(bus.is_connected(1, 0, LEFT));
39/// ```
40///
41/// ```
42/// use tis_100::core::Port::*;
43/// use tis_100::io::IoBus;
44///
45/// let mut bus = IoBus::new();
46/// bus.connect_half(0, 1, RIGHT);
47///
48/// {
49///     let mut view = bus.view(0);
50///     view.write(RIGHT, 42);
51/// }
52///
53/// bus.commit();
54///
55/// {
56///     let mut view = bus.view(1);
57///     assert_eq!(view.read(LEFT), Some(42));
58/// }
59/// ```
60#[derive(Debug)]
61pub struct IoBus {
62    next_index: PortId,
63    ports: VecMap<isize>,
64    writes: VecMap<isize>,
65    write_blocks: VecMap<isize>,
66    nodes: VecMap<PortMap>,
67}
68
69impl IoBus {
70    /// Construct a new, empty `IoBus`.
71    pub fn new() -> IoBus {
72        IoBus {
73            next_index: 0,
74            ports: VecMap::new(),
75            writes: VecMap::new(),
76            write_blocks: VecMap::new(),
77            nodes: VecMap::new(),
78        }
79    }
80
81    /// Create a one-way connection from one node to another in the given direction.
82    pub fn connect_half(&mut self, from: NodeId, to: NodeId, port: Port) -> &mut Self {
83        let to_port = opposite_port(port);
84
85        // Make sure that a map exists for each map.
86        if !self.map_exists(from) {
87            self.insert_map(from);
88        }
89
90        if !self.map_exists(to) {
91            self.insert_map(to);
92        }
93
94        self.nodes.get_mut(from).unwrap().set_output(port, self.next_index, to);
95        self.nodes.get_mut(to).unwrap().set_input(to_port, self.next_index, from);
96        self.next_index += 1;
97
98        self
99    }
100
101    /// Create a two-way connection from one node to another in the given direction. The opposite
102    /// direction will be used when connecting the second half.
103    pub fn connect_full(&mut self, from: NodeId, to: NodeId, port: Port) -> &mut Self {
104        self.connect_half(from, to, port)
105            .connect_half(to, from, opposite_port(port))
106    }
107
108    /// Determine if two nodes are connected in a certain direction.
109    pub fn is_connected(&self, from: NodeId, to: NodeId, port: Port) -> bool {
110        // Two nodes are connected if each node has a connection to the other node in opposing
111        // directions.
112        if let Some(map) = self.nodes.get(from) {
113            if let Some(&Connection(_, to_node)) = map.get_output(port) {
114                if let Some(map) = self.nodes.get(to) {
115                    let to_port = opposite_port(port);
116                    if let Some(&Connection(_, from_node)) = map.get_output(to_port) {
117                        return to_node == to && from_node == from;
118                    }
119                }
120            }
121        }
122
123        false
124    }
125
126    /// Returns a view of the `IoBus` for the given node.
127    pub fn view<'a>(&'a mut self, node: NodeId) -> IoBusView<'a> {
128        assert!(self.nodes.get(node).is_some());
129        IoBusView::new(self, node)
130    }
131
132    /// Commits all outstanding writes and clears the write buffer.
133    pub fn commit(&mut self) {
134        for (i, &v) in self.writes.iter() {
135            self.ports.insert(i, v);
136        }
137
138        self.writes.clear();
139    }
140
141    /// Send data on a given port for a node.
142    fn write(&mut self, node: NodeId, port: Port, value: isize) {
143        if let Some(&Connection(index, _)) = self.get_output(node, port) {
144            self.writes.insert(index, value);
145
146            // Writing to the IoBus causes a node to block until the value has been consumed by a
147            // read.
148            self.write_blocks.insert(node, value);
149        }
150    }
151
152    /// Check if an output port has been read for a node.
153    fn is_blocked(&self, node: NodeId) -> bool {
154        self.write_blocks.get(node).is_some()
155    }
156
157    /// Receive data on a given port for a node. Whenever a node reads from an input, all of the
158    /// outputs on the sending node are cleared.
159    fn read(&mut self, node: NodeId, port: Port) -> Option<isize> {
160        if let Some(&Connection(index, out_node)) = self.get_input(node, port) {
161            if let Some(val) = self.ports.remove(index) {
162                self.clear_outputs(out_node);
163                self.write_blocks.remove(out_node);
164                return Some(val);
165            }
166        }
167
168        None
169    }
170
171    /// Get an input connection from a `PortMap`.
172    fn get_input(&self, node: NodeId, port: Port) -> Option<&Connection> {
173        if let Some(map) = self.nodes.get(node) {
174            map.get_input(port)
175        } else {
176            None
177        }
178    }
179
180    /// Get an output connection from a `PortMap`.
181    fn get_output(&self, node: NodeId, port: Port) -> Option<&Connection> {
182        if let Some(map) = self.nodes.get(node) {
183            map.get_output(port)
184        } else {
185            None
186        }
187    }
188
189    /// Create a new `PortMap`.
190    fn insert_map(&mut self, node: NodeId) {
191        self.nodes.insert(node, PortMap::new());
192    }
193
194    /// Check if a `PortMap` exists.
195    fn map_exists(&self, node: NodeId) -> bool {
196        self.nodes.get(node).is_some()
197    }
198
199    /// Clear all of the output ports for a given node.
200    fn clear_outputs(&mut self, node: NodeId) {
201        let to_clear = match self.nodes.get(node) {
202            Some(map) => map.output_iter()
203                            .map(|(_, &Connection(i, _))| { i })
204                            .collect::<Vec<_>>(),
205            None => Vec::new(),
206        };
207
208        for index in to_clear.iter() {
209            self.ports.remove(*index);
210        }
211    }
212}
213
214/// Provides access to the `IoBus` for a single node. This ensures that nodes are only able to read
215/// and write on ports that they are connected to.
216#[derive(Debug)]
217pub struct IoBusView<'a> {
218    bus: &'a mut IoBus,
219    node: usize,
220}
221
222impl<'a> IoBusView<'a> {
223    /// Construct a new `IoBusView`.
224    fn new(bus: &'a mut IoBus, node: usize) -> IoBusView<'a> {
225        IoBusView {
226            bus: bus,
227            node: node,
228        }
229    }
230
231    /// Receive data on a given port.
232    pub fn read(&mut self, port: Port) -> Option<isize> {
233        self.bus.read(self.node, port)
234    }
235
236    /// Send data on a given port.
237    pub fn write(&mut self, port: Port, value: isize) {
238        self.bus.write(self.node, port, value);
239    }
240
241    /// Check if an output port has been read.
242    pub fn is_blocked(&self) -> bool {
243        self.bus.is_blocked(self.node)
244    }
245}
246
247/// For a given node, this maps from an input or output port direction to the bus index containing
248/// the data for that direction.
249#[derive(Debug)]
250struct PortMap {
251    input: HashMap<Port, Connection>,
252    output: HashMap<Port, Connection>,
253}
254
255impl PortMap {
256    /// Construct a new, empty `PortMap`.
257    fn new() -> PortMap {
258        PortMap {
259            input: HashMap::new(),
260            output: HashMap::new(),
261        }
262    }
263
264    /// Returns an iterator over the outputs.
265    fn output_iter(&self) -> Iter<Port, Connection> {
266        self.output.iter()
267    }
268
269    /// Set the input index for a given port direction. We also store the node that owns the
270    /// corresponding output so that we can clear its outputs after a read.
271    fn set_input(&mut self, port: Port, index: PortId, node: NodeId) {
272        self.input.insert(port, Connection(index, node));
273    }
274
275    /// Get the input index for a given port direction. We also return the node that owns the
276    /// corresponding output so that we can clear its outputs after a read.
277    fn get_input(&self, port: Port) -> Option<&Connection> {
278        self.input.get(&port)
279    }
280
281    /// Set the output index for a given port direction.
282    fn set_output(&mut self, port: Port, index: PortId, node: NodeId) {
283        self.output.insert(port, Connection(index, node));
284    }
285
286    /// Get the output index for a given port direction.
287    fn get_output(&self, port: Port) -> Option<&Connection> {
288        self.output.get(&port)
289    }
290}