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}