use std::hash::Hash;
use crate::anomaly::sink::AnomalySink;
use crate::correlate::TimeBucketedCounter;
use crate::ctx::Ctx;
impl Ctx<'_> {
pub fn split_state_sink<T>(&mut self) -> (&mut T, &mut dyn AnomalySink)
where
T: Default + Send + 'static,
{
let state_ptr: *mut T = self.state_map.get_or_init_mut::<T>() as *mut T;
let sink_ptr: *mut dyn AnomalySink = self.sink;
unsafe { (&mut *state_ptr, &mut *sink_ptr) }
}
pub fn split_state_counter<T, K>(&mut self) -> (&mut T, &mut TimeBucketedCounter<K>)
where
T: Default + Send + 'static,
K: Hash + Eq + Clone + Send + 'static,
{
let state_ptr: *mut T = self.state_map.get_or_init_mut::<T>() as *mut T;
let counter_ptr: *mut TimeBucketedCounter<K> =
self.counters.get_mut::<K>() as *mut TimeBucketedCounter<K>;
unsafe { (&mut *state_ptr, &mut *counter_ptr) }
}
pub fn split_sink_counter<K>(&mut self) -> (&mut dyn AnomalySink, &mut TimeBucketedCounter<K>)
where
K: Hash + Eq + Clone + Send + 'static,
{
let sink_ptr: *mut dyn AnomalySink = self.sink;
let counter_ptr: *mut TimeBucketedCounter<K> =
self.counters.get_mut::<K>() as *mut TimeBucketedCounter<K>;
unsafe { (&mut *sink_ptr, &mut *counter_ptr) }
}
pub fn split_state_sink_counter<T, K>(
&mut self,
) -> (&mut T, &mut dyn AnomalySink, &mut TimeBucketedCounter<K>)
where
T: Default + Send + 'static,
K: Hash + Eq + Clone + Send + 'static,
{
let state_ptr: *mut T = self.state_map.get_or_init_mut::<T>() as *mut T;
let sink_ptr: *mut dyn AnomalySink = self.sink;
let counter_ptr: *mut TimeBucketedCounter<K> =
self.counters.get_mut::<K>() as *mut TimeBucketedCounter<K>;
unsafe { (&mut *state_ptr, &mut *sink_ptr, &mut *counter_ptr) }
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use flowscope::Timestamp;
use super::*;
use crate::anomaly::Severity;
use crate::anomaly::sink::NoopSink;
use crate::ctx::{CounterRegistry, SourceIdx, StateMap};
#[derive(Default)]
struct State1 {
n: u64,
}
#[derive(Default)]
struct State2 {
m: u32,
}
fn make_ctx<'a>(
state: &'a mut StateMap,
sink: &'a mut NoopSink,
counters: &'a mut CounterRegistry,
flow_states: &'a mut crate::ctx::FlowStateRegistry,
) -> Ctx<'a> {
Ctx {
flow: None,
ts: Timestamp::new(0, 0),
source: SourceIdx(0),
monitor_name: None,
state_map: state,
sink,
counters,
flow_states,
label_table: crate::ctx::default_label_table(),
tracker: None,
arp_table: None,
}
}
#[test]
fn split_state_sink_yields_disjoint_borrows() {
let mut state = StateMap::default();
let mut sink = NoopSink;
let mut counters = CounterRegistry::default();
let mut flow_states = crate::ctx::FlowStateRegistry::default();
let mut ctx = make_ctx(&mut state, &mut sink, &mut counters, &mut flow_states);
let (s, k) = ctx.split_state_sink::<State1>();
s.n = 9;
k.write("k", Severity::Info, Timestamp::new(0, 0), None, &[], &[]);
assert_eq!(s.n, 9);
}
#[test]
fn split_state_counter_yields_disjoint_borrows() {
let mut state = StateMap::default();
let mut sink = NoopSink;
let mut counters = CounterRegistry::default();
counters.register::<u32>(TimeBucketedCounter::<u32>::new_unbounded(
Duration::from_secs(10),
Duration::from_secs(1),
));
let mut flow_states = crate::ctx::FlowStateRegistry::default();
let mut ctx = make_ctx(&mut state, &mut sink, &mut counters, &mut flow_states);
let (s, c) = ctx.split_state_counter::<State1, u32>();
s.n = 7;
c.bump(1u32, Timestamp::new(0, 0));
assert_eq!(s.n, 7);
}
#[test]
fn split_sink_counter_yields_disjoint_borrows() {
let mut state = StateMap::default();
let mut sink = NoopSink;
let mut counters = CounterRegistry::default();
counters.register::<u16>(TimeBucketedCounter::<u16>::new_unbounded(
Duration::from_secs(10),
Duration::from_secs(1),
));
let mut flow_states = crate::ctx::FlowStateRegistry::default();
let mut ctx = make_ctx(&mut state, &mut sink, &mut counters, &mut flow_states);
let (k, c) = ctx.split_sink_counter::<u16>();
c.bump(42u16, Timestamp::new(0, 0));
k.write("k", Severity::Info, Timestamp::new(0, 0), None, &[], &[]);
}
#[test]
fn split_state_sink_counter_yields_three_disjoint_borrows() {
let mut state = StateMap::default();
let mut sink = NoopSink;
let mut counters = CounterRegistry::default();
counters.register::<u64>(TimeBucketedCounter::<u64>::new_unbounded(
Duration::from_secs(10),
Duration::from_secs(1),
));
let mut flow_states = crate::ctx::FlowStateRegistry::default();
let mut ctx = make_ctx(&mut state, &mut sink, &mut counters, &mut flow_states);
let (s, k, c) = ctx.split_state_sink_counter::<State2, u64>();
s.m = 11;
c.bump(99u64, Timestamp::new(0, 0));
k.write("k", Severity::Info, Timestamp::new(0, 0), None, &[], &[]);
assert_eq!(s.m, 11);
}
#[test]
fn sequential_split_calls_compose() {
let mut state = StateMap::default();
let mut sink = NoopSink;
let mut counters = CounterRegistry::default();
let mut flow_states = crate::ctx::FlowStateRegistry::default();
let mut ctx = make_ctx(&mut state, &mut sink, &mut counters, &mut flow_states);
{
let (s, _k) = ctx.split_state_sink::<State1>();
s.n = 1;
}
{
let (s, _k) = ctx.split_state_sink::<State1>();
assert_eq!(s.n, 1);
s.n = 2;
}
}
}