iris_core/conntrack/conn/
conn_actions.rs1use super::conn_state::{StateTransition, NUM_STATE_TRANSITIONS};
2use bitmask_enum::bitmask;
3
4#[bitmask(u8)]
6#[bitmask_config(vec_debug)]
7pub enum Actions {
8 Update,
11 Parse,
17 PassThrough,
20 Track,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
27pub struct TrackedActions {
28 pub active: Actions,
30 pub refresh_at: [Actions; NUM_STATE_TRANSITIONS],
32}
33
34fn fmt_actions(actions: Actions) -> String {
35 if actions.is_none() {
36 return "-".to_string();
37 }
38 let mut parts: Vec<&'static str> = Vec::new();
39 if actions.intersects(Actions::Update) {
40 parts.push("Update");
41 }
42 if actions.intersects(Actions::Parse) {
43 parts.push("Parse");
44 }
45 if actions.intersects(Actions::PassThrough) {
46 parts.push("PassThrough");
47 }
48 if actions.intersects(Actions::Track) {
49 parts.push("Track");
50 }
51 parts.join(",")
52}
53
54impl Default for TrackedActions {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl TrackedActions {
62 pub fn new() -> Self {
64 Self {
65 active: Actions { bits: 0 },
66 refresh_at: [Actions { bits: 0 }; NUM_STATE_TRANSITIONS],
67 }
68 }
69
70 #[inline]
75 pub(crate) fn start_state_tx(&mut self, state: StateTransition) {
76 self.active &= self.refresh_at[state.as_usize()].not();
77 self.active &= (Actions::PassThrough).not();
78 }
79
80 #[inline]
82 pub fn clear(&mut self, actions: &Actions) {
83 self.active &= actions.not();
84 }
85
86 #[inline]
88 pub(crate) fn clear_intersection(&mut self, peer: &TrackedActions) {
89 self.clear(&peer.active);
90 for i in 0..NUM_STATE_TRANSITIONS {
91 self.refresh_at[i] &= peer.refresh_at[i].not();
92 }
93 }
94
95 #[inline]
97 pub fn drop(&self) -> bool {
98 self.active.is_none()
99 }
100
101 #[allow(dead_code)]
105 #[inline]
106 pub(crate) fn has_next_layer(&self) -> bool {
107 self.active.intersects(Actions::PassThrough)
108 }
109
110 #[inline]
112 pub(crate) fn set_next_layer(&mut self) {
113 self.active |= Actions::PassThrough;
114 }
115
116 #[inline]
117 pub(crate) fn needs_parse(&self) -> bool {
118 self.active.intersects(Actions::Parse)
119 }
120
121 #[inline]
122 pub(crate) fn needs_update(&self) -> bool {
123 self.active.intersects(Actions::Update)
124 }
125
126 #[inline]
129 pub fn extend(&mut self, other: &TrackedActions) {
130 self.active |= other.active;
131 for i in 0..NUM_STATE_TRANSITIONS {
132 self.refresh_at[i] |= other.refresh_at[i];
133 }
134 }
135
136 pub fn set_terminal_action(&mut self, action: &Actions) {
140 for i in 0..NUM_STATE_TRANSITIONS {
141 self.refresh_at[i] &= action.not();
142 }
143 }
144
145 pub(crate) fn skip_tx(&self, tx: &StateTransition) -> bool {
148 self.refresh_at[tx.as_usize()].is_none()
149 }
150}
151
152impl std::fmt::Display for TrackedActions {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 if self.active.is_none() {
155 return write!(f, "-");
156 }
157
158 let active_str = fmt_actions(self.active);
159 let mut state_txs: Vec<(String, Actions)> = Vec::new();
160 for i in 0..NUM_STATE_TRANSITIONS {
161 let a = self.refresh_at[i];
162 if !a.is_none() {
163 state_txs.push((StateTransition::from_usize(i).to_string(), a));
164 }
165 }
166
167 assert!(
168 !state_txs.is_empty(),
169 "Active actions but no refresh points?"
170 );
171
172 if !self.active.is_none() && state_txs.iter().all(|(_, a)| *a == self.active) {
174 let state_tx_list = state_txs
175 .into_iter()
176 .map(|(u, _)| u)
177 .collect::<Vec<_>>()
178 .join(",");
179 return write!(f, "{}->({})", active_str, state_tx_list);
180 }
181
182 let mut state_tx_list: Vec<Actions> = Vec::new();
184 for (_, a) in &state_txs {
185 if !state_tx_list.contains(a) {
186 state_tx_list.push(*a);
187 }
188 }
189 let action_set = state_tx_list
190 .into_iter()
191 .map(fmt_actions)
192 .collect::<Vec<_>>()
193 .join(",");
194 let state_tx_list = state_txs
195 .into_iter()
196 .map(|(u, a)| format!("{}={}", u, fmt_actions(a)))
197 .collect::<Vec<_>>()
198 .join(",");
199 write!(f, "{}->({})", action_set, state_tx_list)
200 }
201}