1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/* binaryringq.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! A simple Ring Queue that queues binary (on/off) events.

// Imports ===================================================================
use avr_oxide::concurrency::{interrupt, Isolated};
use avr_oxide::OxideResult;
use avr_oxide::sync::EventWait;
use avr_oxide::private::ringq;
use avr_oxide::OxideResult::{Ok,Err};

// Declarations ==============================================================
/// A simple ring queue that queues up to 8 binary (on/off) events.
pub struct BinaryRingQ
{
  length: u8,
  items: u8,
  blocked_consumers: EventWait
}

// Code ======================================================================
impl BinaryRingQ {
  pub fn new() -> Self {
    BinaryRingQ {
      length: 0,
      items: 0,
      blocked_consumers: EventWait::new()
    }
  }

  pub fn consume_blocking(&mut self) -> bool {
    loop {
      let value = interrupt::isolated(|_isotoken|{
        if self.length == 0 {
          None
        } else {
          let bit = (self.items & 0b1) as u8;
          self.items >>= 1;
          self.length -= 1;
          Some(bit != 0)
        }
      });

      match value {
        None => {
          self.blocked_consumers.wait();
        },
        Some(value) => {
          return value;
        }
      }
    }
  }

  pub fn append(&mut self, _isotoken: Isolated, element: bool) -> OxideResult<(),ringq::QueueError> {
    if self.length >= 8 {
      Err(ringq::QueueError::QueueFull)
    } else {
      if element {
        let bit = 0b1u8 << self.length;
        self.items |= bit;
      }
      self.length += 1;

      self.blocked_consumers.release_one();

      Ok(())
    }
  }
}

// Tests =====================================================================
#[cfg(test)]
mod tests {
  use avr_oxide::private::binaryringq::BinaryRingQ;

  #[test]
  fn test_binaryringq() {
    let mut testq = BinaryRingQ::new();

    avr_oxide::concurrency::interrupt::isolated(|isotoken|{
      // Check we can insert stuff up to the limit
      assert!(testq.append(isotoken, true).is_ok());
      assert!(testq.append(isotoken, false).is_ok());
      assert!(testq.append(isotoken, false).is_ok());
      assert!(testq.append(isotoken, true).is_ok());
      assert!(testq.append(isotoken, true).is_ok());
      assert!(testq.append(isotoken, true).is_ok());
      assert!(testq.append(isotoken, false).is_ok());
      assert!(testq.append(isotoken, true).is_ok());
      // But no further
      assert!(testq.append(isotoken, true).is_err());

      // Now check it comes back out the same :)
      assert_eq!(testq.consume_blocking(), true);
      assert_eq!(testq.consume_blocking(), false);
      assert_eq!(testq.consume_blocking(), false);
      assert_eq!(testq.consume_blocking(), true);
      assert_eq!(testq.consume_blocking(), true);
      assert_eq!(testq.consume_blocking(), true);
      assert_eq!(testq.consume_blocking(), false);
      assert_eq!(testq.consume_blocking(), true);
    });
  }
}