diff --git a/server/db/days.rs b/server/db/days.rs index 7d8a51f..f4ad104 100644 --- a/server/db/days.rs +++ b/server/db/days.rs @@ -7,8 +7,10 @@ use crate::recording::{self, Time}; use failure::Error; use log::{error, trace}; +use smallvec::SmallVec; use std::cmp; use std::collections::BTreeMap; +use std::convert::TryFrom; use std::io::Write; use std::ops::Range; use std::str; @@ -78,6 +80,72 @@ impl Value for StreamValue { } } +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct SignalValue { + /// `states[i]` represents the amount of time spent in state `i+1`. + /// (The signal is the unknown state, 0, for the remainder of the time.) + pub states: SmallVec<[u32; 4]>, +} + +impl Value for SignalValue { + type Change = SignalChange; + + fn apply(&mut self, c: &SignalChange) { + if self.states.len() < usize::try_from(c.new_state).unwrap() { + self.states.resize(c.new_state as usize, 0); + } + + if c.new_state > 0 { + // add to new state. + let s = &mut self.states[c.new_state as usize - 1]; + let n = s + .checked_add(c.duration) + .unwrap_or_else(|| panic!("add range violation: s={:?} c={:?}", s, c)); + *s = n; + } + + if c.old_state > 0 { + // remove from old state. + let i = usize::try_from(c.old_state).unwrap() - 1; + assert!( + self.states.len() > i, + "no such old state: s={:?} c={:?}", + self, + c + ); + let s = &mut self.states[c.old_state as usize - 1]; + let n = s + .checked_sub(c.duration) + .unwrap_or_else(|| panic!("sub range violation: s={:?} c={:?}", s, c)); + *s = n; + } + + // Normalize. + let mut l = self.states.len(); + while l > 0 && self.states[l - 1] == 0 { + l -= 1; + } + self.states.truncate(l); + } + + fn is_empty(&self) -> bool { + self.states.is_empty() + } +} + +/// A change to a signal within a single day. +#[derive(Debug)] +pub struct SignalChange { + /// The duration of time being altered. + duration: u32, + + /// The state of the given range before this change. + old_state: i16, + + /// The state of the given range after this change. + new_state: i16, +} + #[derive(Clone, Debug)] pub struct Map(BTreeMap); @@ -129,16 +197,14 @@ impl Map { /// not much that can be done about them. (The database operation has already gone through.) pub(crate) fn adjust(&mut self, r: Range