1use std::cell::RefCell;
25use std::future::Future;
26use std::pin::Pin;
27use std::rc::Rc;
28
29use rustdv_sim::queue::Queue;
30
31use crate::component::{Component, ComponentNode};
32use crate::port::{bind_or_panic, sink_of, GetIf, PeekIf, PortName, PortOwner, PutIf, SinkHandle};
33
34struct FifoInner<T: 'static> {
42 q: Queue<T>,
43 size: Option<usize>,
44 put_taps: RefCell<Vec<Rc<dyn SinkHandle<T>>>>,
48 get_taps: RefCell<Vec<Rc<dyn SinkHandle<T>>>>,
49}
50
51impl<T: 'static> FifoInner<T> {
52 fn new(size: Option<usize>) -> FifoInner<T> {
53 FifoInner {
54 q: match size {
55 Some(n) => Queue::new(Some(n)),
56 None => Queue::unbounded(),
57 },
58 size,
59 put_taps: RefCell::new(Vec::new()),
60 get_taps: RefCell::new(Vec::new()),
61 }
62 }
63
64 fn is_full(&self) -> bool {
65 match self.size {
66 None => false,
67 Some(s) => self.q.len() >= s,
68 }
69 }
70
71 fn tap(taps: &RefCell<Vec<Rc<dyn SinkHandle<T>>>>, item: &T) {
74 let subs: Vec<Rc<dyn SinkHandle<T>>> = taps.borrow().clone();
75 for sub in subs {
76 sub.deliver(item);
77 }
78 }
79
80 async fn put_tapped(&self, item: T) {
87 self.q.wait_for_space().await;
88 Self::tap(&self.put_taps, &item);
89 let _ = self.q.try_put(item);
90 }
91
92 fn try_put_tapped(&self, item: T) -> Result<(), T> {
93 if !self.q.has_space() {
94 return Err(item);
95 }
96 Self::tap(&self.put_taps, &item);
97 self.q.try_put(item)
98 }
99
100 fn tap_get(&self, item: Option<T>) -> Option<T> {
101 if let Some(v) = &item {
102 Self::tap(&self.get_taps, v);
103 }
104 item
105 }
106}
107
108impl<T: 'static> PutIf<T> for FifoInner<T> {
109 fn put(&self, item: T) -> Pin<Box<dyn Future<Output = ()> + '_>> {
110 Box::pin(self.put_tapped(item))
111 }
112 fn try_put(&self, item: T) -> Result<(), T> {
113 self.try_put_tapped(item)
114 }
115 fn can_put(&self) -> bool {
116 !self.is_full()
117 }
118}
119
120impl<T: 'static> GetIf<T> for FifoInner<T> {
121 fn get(&self) -> Pin<Box<dyn Future<Output = T> + '_>> {
122 Box::pin(async move {
123 let item = self.q.get().await;
124 Self::tap(&self.get_taps, &item);
125 item
126 })
127 }
128 fn try_get(&self) -> Option<T> {
129 let item = self.q.try_get();
130 self.tap_get(item)
131 }
132 fn can_get(&self) -> bool {
133 !self.q.is_empty()
134 }
135}
136
137impl<T: Clone + 'static> PeekIf<T> for FifoInner<T> {
138 fn peek(&self) -> Pin<Box<dyn Future<Output = T> + '_>> {
139 Box::pin(self.q.peek())
140 }
141 fn try_peek(&self) -> Option<T> {
142 self.q.try_peek()
143 }
144 fn can_peek(&self) -> bool {
145 !self.q.is_empty()
146 }
147}
148
149pub struct PutExport<T: 'static> {
158 iface: Rc<dyn PutIf<T>>,
159}
160
161impl<T: 'static> PutExport<T> {
162 pub fn connect(&self, owner: &dyn PortOwner, name: PortName<dyn PutIf<T>>) {
168 bind_or_panic(owner, name, self.iface.clone());
169 }
170}
171
172pub struct GetExport<T: 'static> {
174 iface: Rc<dyn GetIf<T>>,
175}
176
177impl<T: 'static> GetExport<T> {
178 pub fn connect(&self, owner: &dyn PortOwner, name: PortName<dyn GetIf<T>>) {
179 bind_or_panic(owner, name, self.iface.clone());
180 }
181}
182
183pub struct TapExport<T: 'static> {
186 taps: Rc<FifoInner<T>>,
187 on_put: bool,
188}
189
190impl<T: 'static> TapExport<T> {
191 pub fn connect(&self, owner: &dyn PortOwner, name: PortName<dyn SinkHandle<T>>) {
194 match sink_of(owner, name) {
195 Ok(sink) => {
196 let list = if self.on_put { &self.taps.put_taps } else { &self.taps.get_taps };
197 list.borrow_mut().push(sink);
198 }
199 Err(e) => panic!("{e}"),
200 }
201 }
202}
203
204pub struct PeekExport<T: 'static> {
206 iface: Rc<dyn PeekIf<T>>,
207}
208
209impl<T: 'static> PeekExport<T> {
210 pub fn connect(&self, owner: &dyn PortOwner, name: PortName<dyn PeekIf<T>>) {
211 bind_or_panic(owner, name, self.iface.clone());
212 }
213}
214
215pub struct TlmFifo<T: 'static> {
224 inner: Rc<FifoInner<T>>,
225}
226
227impl<T: 'static> Default for TlmFifo<T> {
228 fn default() -> Self {
231 TlmFifo::new(1)
232 }
233}
234
235impl<T: 'static> TlmFifo<T> {
236 pub fn new(size: usize) -> TlmFifo<T> {
239 TlmFifo { inner: Rc::new(FifoInner::new(Some(size))) }
240 }
241
242 pub fn unbounded() -> TlmFifo<T> {
244 TlmFifo { inner: Rc::new(FifoInner::new(None)) }
245 }
246
247 pub fn size(&self) -> Option<usize> {
249 self.inner.size
250 }
251 pub fn used(&self) -> usize {
253 self.inner.q.len()
254 }
255 pub fn is_empty(&self) -> bool {
256 self.inner.q.is_empty()
257 }
258 pub fn is_full(&self) -> bool {
259 self.inner.is_full()
260 }
261 pub fn flush(&self) {
263 while self.inner.q.try_get().is_some() {}
264 }
265
266 pub fn put_export(&self) -> PutExport<T> {
270 PutExport { iface: self.inner.clone() }
271 }
272
273 pub fn get_export(&self) -> GetExport<T> {
275 GetExport { iface: self.inner.clone() }
276 }
277
278 pub fn put_ap(&self) -> TapExport<T> {
280 TapExport { taps: self.inner.clone(), on_put: true }
281 }
282
283 pub fn get_ap(&self) -> TapExport<T> {
285 TapExport { taps: self.inner.clone(), on_put: false }
286 }
287
288 pub async fn put(&self, item: T) {
294 self.inner.put_tapped(item).await
295 }
296 pub fn try_put(&self, item: T) -> Result<(), T> {
297 self.inner.try_put_tapped(item)
298 }
299 pub async fn get(&self) -> T {
300 let item = self.inner.q.get().await;
301 FifoInner::tap(&self.inner.get_taps, &item);
302 item
303 }
304 pub fn try_get(&self) -> Option<T> {
305 let item = self.inner.q.try_get();
306 self.inner.tap_get(item)
307 }
308
309 #[cfg(test)]
312 pub(crate) fn put_iface_for_test(&self) -> Rc<dyn PutIf<T>> {
313 self.inner.clone()
314 }
315
316 pub fn handle(&self) -> TlmFifo<T> {
318 TlmFifo { inner: self.inner.clone() }
319 }
320}
321
322impl<T: Clone + 'static> TlmFifo<T> {
323 pub fn peek_export(&self) -> PeekExport<T> {
326 PeekExport { iface: self.inner.clone() }
327 }
328
329 pub async fn peek(&self) -> T {
330 self.inner.q.peek().await
331 }
332 pub fn try_peek(&self) -> Option<T> {
333 self.inner.q.try_peek()
334 }
335}
336
337impl<T: 'static> Component for TlmFifo<T> {}
340
341impl<T: 'static> ComponentNode for TlmFifo<T> {
342 fn node_name(&self) -> &'static str {
343 "TlmFifo"
344 }
345 fn children_mut(&mut self) -> Vec<(String, &mut (dyn ComponentNode + 'static))> {
346 Vec::new()
347 }
348}
349
350#[cfg(test)]
355mod tests {
356 use super::*;
357 use crate::port::{
358 GetPort, PeekPort, PortField, PortName, PortOwner, PutPort, SubscribePort, Subscriber,
359 };
360 use crate::shared::RustdvShared;
361 use rustdv_sim::testing::block_on;
362 use std::any::Any;
363
364 struct Holder {
367 put: PutPort<u8>,
368 get: GetPort<u8>,
369 peek: PeekPort<u8>,
370 sub: SubscribePort<u8>,
371 }
372
373 impl Holder {
374 fn new() -> Holder {
375 Holder {
376 put: PutPort::default(),
377 get: GetPort::default(),
378 peek: PeekPort::default(),
379 sub: SubscribePort::default(),
380 }
381 }
382 const PUT: PortName<dyn PutIf<u8>> = PortName::new("put");
383 const GET: PortName<dyn GetIf<u8>> = PortName::new("get");
384 const PEEK: PortName<dyn PeekIf<u8>> = PortName::new("peek");
385 const SUB: PortName<dyn SinkHandle<u8>> = PortName::new("sub");
386 }
387
388 impl PortOwner for Holder {
389 fn owner_port_slot(&self, name: &str) -> Option<Rc<dyn Any>> {
390 match name {
391 "put" => Some(self.put.slot_any()),
392 "get" => Some(self.get.slot_any()),
393 "peek" => Some(self.peek.slot_any()),
394 "sub" => Some(self.sub.slot_any()),
395 _ => None,
396 }
397 }
398 fn owner_label(&self) -> &'static str {
399 "Holder"
400 }
401 }
402
403 #[test]
404 fn a_port_is_unbound_until_connected() {
405 let h = Holder::new();
406 assert!(!h.put.bound());
407 let fifo: TlmFifo<u8> = TlmFifo::new(1);
408 fifo.put_export().connect(&h, Holder::PUT);
409 assert!(h.put.bound());
410 }
411
412 #[test]
413 fn put_and_get_through_a_fifo() {
414 block_on(async {
415 let h = Holder::new();
416 let fifo: TlmFifo<u8> = TlmFifo::new(2);
417 fifo.put_export().connect(&h, Holder::PUT);
418 fifo.get_export().connect(&h, Holder::GET);
419
420 h.put.put(1).await;
421 h.put.put(2).await;
422 assert_eq!(h.get.get().await, 1, "FIFO order through the ports");
423 assert_eq!(h.get.get().await, 2);
424 });
425 }
426
427 #[test]
428 fn peek_leaves_the_item_for_get() {
429 block_on(async {
430 let h = Holder::new();
431 let fifo: TlmFifo<u8> = TlmFifo::new(1);
432 fifo.put_export().connect(&h, Holder::PUT);
433 fifo.peek_export().connect(&h, Holder::PEEK);
434 fifo.get_export().connect(&h, Holder::GET);
435
436 h.put.put(9).await;
437 assert_eq!(h.peek.peek().await, 9);
438 assert_eq!(h.get.get().await, 9, "peek did not consume it");
439 });
440 }
441
442 #[test]
444 fn try_put_hands_a_refused_item_back() {
445 block_on(async {
446 let h = Holder::new();
447 let fifo: TlmFifo<u8> = TlmFifo::new(1);
448 fifo.put_export().connect(&h, Holder::PUT);
449 assert!(h.put.try_put(1).is_ok());
450 assert_eq!(h.put.try_put(2), Err(2), "the item comes home");
451 });
452 }
453
454 #[test]
455 fn can_put_and_can_get_track_the_fifo() {
456 block_on(async {
457 let h = Holder::new();
458 let fifo: TlmFifo<u8> = TlmFifo::new(1);
459 fifo.put_export().connect(&h, Holder::PUT);
460 fifo.get_export().connect(&h, Holder::GET);
461 assert!(h.put.can_put());
462 assert!(!h.get.can_get());
463 h.put.put(1).await;
464 assert!(!h.put.can_put());
465 assert!(h.get.can_get());
466 });
467 }
468
469 #[test]
470 fn a_bad_port_name_is_a_named_error() {
471 let h = Holder::new();
472 let nope: PortName<dyn PutIf<u8>> = PortName::new("no_such_port");
473 let fifo: TlmFifo<u8> = TlmFifo::new(1);
474 match crate::port::bind(&h, nope, fifo.put_iface_for_test()) {
475 Err(crate::port::ConnectError::NoSuchPort { owner, name }) => {
476 assert_eq!(owner, "Holder");
477 assert_eq!(name, "no_such_port");
478 }
479 other => panic!("expected NoSuchPort, got {other:?}"),
480 }
481 }
482
483 #[test]
484 fn fifo_size_used_and_flush() {
485 block_on(async {
486 let fifo: TlmFifo<u8> = TlmFifo::new(3);
487 assert_eq!(fifo.size(), Some(3));
488 assert!(fifo.is_empty());
489 fifo.put(1).await;
490 fifo.put(2).await;
491 assert_eq!(fifo.used(), 2);
492 fifo.flush();
493 assert!(fifo.is_empty(), "flush empties it");
494 });
495 }
496
497 #[test]
498 fn unbounded_is_never_full() {
499 block_on(async {
500 let fifo: TlmFifo<u8> = TlmFifo::unbounded();
501 assert_eq!(fifo.size(), None);
502 for n in 0..100 {
503 assert!(fifo.try_put(n).is_ok());
504 }
505 assert!(!fifo.is_full());
506 });
507 }
508
509 #[test]
512 fn the_put_tap_sees_every_item_and_consumes_none() {
513 #[derive(Default)]
514 struct Log {
515 seen: Vec<u8>,
516 }
517 impl Subscriber<u8> for Log {
518 fn write(&mut self, item: &u8) {
519 self.seen.push(*item);
520 }
521 }
522
523 block_on(async {
524 let h = Holder::new();
525 let log: RustdvShared<Log> = RustdvShared::default();
526 h.sub.subscribe(log.clone());
527
528 let fifo: TlmFifo<u8> = TlmFifo::unbounded();
529 fifo.put_ap().connect(&h, Holder::SUB);
530
531 for n in 1..=3u8 {
532 fifo.put(n).await;
533 }
534 assert_eq!(log.get().seen, vec![1, 2, 3], "the tap saw all three");
535 assert_eq!(fifo.used(), 3, "and took none of them");
536 });
537 }
538
539 #[test]
540 fn the_get_tap_fires_as_items_leave() {
541 #[derive(Default)]
542 struct Log {
543 seen: Vec<u8>,
544 }
545 impl Subscriber<u8> for Log {
546 fn write(&mut self, item: &u8) {
547 self.seen.push(*item);
548 }
549 }
550
551 block_on(async {
552 let h = Holder::new();
553 let log: RustdvShared<Log> = RustdvShared::default();
554 h.sub.subscribe(log.clone());
555
556 let fifo: TlmFifo<u8> = TlmFifo::unbounded();
557 fifo.get_ap().connect(&h, Holder::SUB);
558 fifo.put(7).await;
559 assert!(log.get().seen.is_empty(), "nothing has left yet");
560 let _ = fifo.get().await;
561 assert_eq!(log.get().seen, vec![7]);
562 });
563 }
564
565 #[test]
566 fn a_handle_is_the_same_fifo() {
567 block_on(async {
568 let fifo: TlmFifo<u8> = TlmFifo::unbounded();
569 let other = fifo.handle();
570 fifo.put(1).await;
571 assert_eq!(other.used(), 1, "two handles, one FIFO");
572 });
573 }
574
575}