Skip to main content

telltale_vm/vm/runtime_state/
resources.rs

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