Skip to main content

telltale_machine/engine/runtime_state/
resources.rs

1/// Runtime arena with slot reuse.
2#[allow(dead_code)]
3#[derive(Debug, Clone, Serialize, Deserialize)]
4pub(crate) struct Arena {
5    slots: Vec<Option<Value>>,
6    next_free: usize,
7    capacity: usize,
8}
9
10impl Default for Arena {
11    fn default() -> Self {
12        Self::new(128)
13    }
14}
15
16#[allow(dead_code)]
17impl Arena {
18    /// Construct an arena with the given slot capacity.
19    #[must_use]
20    pub(crate) fn new(capacity: usize) -> Self {
21        let cap = capacity.max(1);
22        Self {
23            slots: vec![None; cap],
24            next_free: 0,
25            capacity: cap,
26        }
27    }
28
29    /// Allocate one slot and return its index.
30    ///
31    /// # Errors
32    ///
33    /// Returns an error when no free slot is available.
34    pub(crate) fn alloc(&mut self, value: Value) -> Result<usize, String> {
35        for offset in 0..self.capacity {
36            let idx = (self.next_free + offset) % self.capacity;
37            if self.slots[idx].is_none() {
38                self.slots[idx] = Some(value);
39                self.next_free = (idx + 1) % self.capacity;
40                debug_assert!(self.check_invariants());
41                return Ok(idx);
42            }
43        }
44        Err("arena full".to_string())
45    }
46
47    /// Free one occupied slot and return its value.
48    ///
49    /// # Errors
50    ///
51    /// Returns an error if the index is invalid or the slot is already free.
52    pub(crate) fn free(&mut self, idx: usize) -> Result<Value, String> {
53        if idx >= self.capacity {
54            return Err("arena index out of bounds".to_string());
55        }
56        let value = self.slots[idx]
57            .take()
58            .ok_or_else(|| "arena slot already free".to_string())?;
59        if idx < self.next_free {
60            self.next_free = idx;
61        }
62        debug_assert!(self.check_invariants());
63        Ok(value)
64    }
65
66    /// Borrow a value in a slot by index.
67    #[must_use]
68    pub(crate) fn get(&self, idx: usize) -> Option<&Value> {
69        self.slots.get(idx).and_then(Option::as_ref)
70    }
71
72    /// Mutably borrow a value in a slot by index.
73    pub(crate) fn get_mut(&mut self, idx: usize) -> Option<&mut Value> {
74        self.slots.get_mut(idx).and_then(Option::as_mut)
75    }
76
77    /// Validate arena structural invariants.
78    #[must_use]
79    pub(crate) fn check_invariants(&self) -> bool {
80        self.slots.len() == self.capacity && self.next_free < self.capacity
81    }
82}
83
84/// Session kind monitored at runtime.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86pub(crate) enum SessionKind {
87    /// Endpoint is acting as a client.
88    Client,
89    /// Endpoint is acting as a server.
90    Server,
91    /// Endpoint is acting as a peer.
92    Peer,
93}
94
95/// Runtime judgment for one monitor check.
96#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
97pub(crate) struct WellTypedInstr {
98    /// Endpoint checked by the monitor.
99    pub endpoint: Endpoint,
100    /// Instruction tag emitted for this check.
101    pub instr_tag: String,
102    /// Tick at which the monitor check occurred.
103    pub tick: u64,
104}
105
106/// Runtime monitor state for session checks.
107#[derive(Debug, Clone, Serialize, Deserialize, Default)]
108pub(crate) struct SessionMonitor {
109    session_kinds: BTreeMap<SessionId, SessionKind>,
110    last_judgment: Option<WellTypedInstr>,
111}
112
113impl SessionMonitor {
114    /// Set the session kind for one session id.
115    pub(crate) fn set_kind(&mut self, sid: SessionId, kind: SessionKind) {
116        self.session_kinds.insert(sid, kind);
117    }
118
119    /// Remove tracked kind metadata for a session id.
120    pub(crate) fn remove_kind(&mut self, sid: SessionId) {
121        self.session_kinds.remove(&sid);
122    }
123
124    /// Record the most recent monitor judgment.
125    pub(crate) fn record(&mut self, endpoint: &Endpoint, instr_tag: &str, tick: u64) {
126        self.last_judgment = Some(WellTypedInstr {
127            endpoint: endpoint.clone(),
128            instr_tag: instr_tag.to_string(),
129            tick,
130        });
131    }
132}
133
134/// Lean-aligned site identifier for failure topology state.
135pub(crate) type SiteId = String;
136
137/// Active corruption policy for one directed edge.
138#[allow(dead_code)]
139#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
140pub(crate) struct CorruptedEdge {
141    edge: Edge,
142    corruption: CorruptionType,
143}
144
145/// Active timeout window for one site.
146#[allow(dead_code)]
147#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
148pub(crate) struct SiteTimeout {
149    site: SiteId,
150    until_tick: u64,
151}
152
153/// Guard layer configuration.
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct GuardLayerConfig {
156    /// Guard layer identifier.
157    pub id: String,
158    /// Whether the layer is active.
159    pub active: bool,
160}
161
162/// Instruction monitor mode for pre-dispatch checks.
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
164pub enum MonitorMode {
165    /// Disable monitor precheck at dispatch.
166    Off,
167    /// Perform session-type-shape monitor precheck before stepping.
168    #[default]
169    SessionTypePrecheck,
170}