Skip to main content

qubit_state_machine/fast/
fast_state_machine_builder.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10
11//! Builder for fast state machine rules.
12
13use crate::FastCasPolicy;
14
15use super::{
16    FastStateMachine,
17    FastStateMachineBuildError,
18};
19use qubit_cas::FastCas;
20
21/// Default retry policy used by [`FastStateMachineBuilder`].
22///
23/// The default keeps construction lightweight and gives a reasonably balanced
24/// fast-path retry budget for hot transition loops.
25pub const FAST_STATE_MACHINE_DEFAULT_CAS_POLICY: FastCasPolicy = FastCasPolicy::spin(16);
26
27/// Builder for dense, integer-coded state machine rules.
28#[derive(Debug, Clone)]
29pub struct FastStateMachineBuilder {
30    state_count: Option<usize>,
31    event_count: Option<usize>,
32    initial_states: Vec<usize>,
33    final_states: Vec<usize>,
34    transitions: Vec<(usize, usize, usize)>,
35    cas_policy: FastCasPolicy,
36}
37
38impl FastStateMachineBuilder {
39    /// Creates an empty builder.
40    pub fn new() -> Self {
41        Self {
42            state_count: None,
43            event_count: None,
44            initial_states: Vec::new(),
45            final_states: Vec::new(),
46            transitions: Vec::new(),
47            cas_policy: FAST_STATE_MACHINE_DEFAULT_CAS_POLICY,
48        }
49    }
50
51    /// Sets the number of state codes used by this machine.
52    pub const fn state_count(mut self, count: usize) -> Self {
53        self.state_count = Some(count);
54        self
55    }
56
57    /// Sets the number of event codes used by this machine.
58    pub const fn event_count(mut self, count: usize) -> Self {
59        self.event_count = Some(count);
60        self
61    }
62
63    /// Registers one initial state code.
64    pub fn initial_state(mut self, state: usize) -> Self {
65        self.initial_states.push(state);
66        self
67    }
68
69    /// Registers multiple initial state codes.
70    pub fn initial_states(mut self, states: &[usize]) -> Self {
71        self.initial_states.extend(states.iter().copied());
72        self
73    }
74
75    /// Registers one final state code.
76    pub fn final_state(mut self, state: usize) -> Self {
77        self.final_states.push(state);
78        self
79    }
80
81    /// Registers multiple final state codes.
82    pub fn final_states(mut self, states: &[usize]) -> Self {
83        self.final_states.extend(states.iter().copied());
84        self
85    }
86
87    /// Adds one transition by source state code, event code, and target state.
88    pub fn transition(mut self, source: usize, event: usize, target: usize) -> Self {
89        self.transitions.push((source, event, target));
90        self
91    }
92
93    /// Sets the retry policy used by [`FastStateMachine`] for CAS conflicts.
94    pub const fn cas_policy(mut self, cas_policy: FastCasPolicy) -> Self {
95        self.cas_policy = cas_policy;
96        self
97    }
98
99    /// Builds and validates an immutable fast state machine.
100    pub fn build(self) -> Result<FastStateMachine, FastStateMachineBuildError> {
101        let state_count = self
102            .state_count
103            .ok_or(FastStateMachineBuildError::StateCountNotConfigured)?;
104        let event_count = self
105            .event_count
106            .ok_or(FastStateMachineBuildError::EventCountNotConfigured)?;
107
108        if state_count == 0 {
109            return Err(FastStateMachineBuildError::InvalidStateCount { count: state_count });
110        }
111        if event_count == 0 {
112            return Err(FastStateMachineBuildError::InvalidEventCount { count: event_count });
113        }
114
115        let transition_count =
116            state_count
117                .checked_mul(event_count)
118                .ok_or(FastStateMachineBuildError::TransitionTableOverflow {
119                    state_count,
120                    event_count,
121                })?;
122
123        let mut initial_states = vec![false; state_count];
124        for state in self.initial_states {
125            if state >= state_count {
126                return Err(FastStateMachineBuildError::InitialStateOutOfRange { state, state_count });
127            }
128            initial_states[state] = true;
129        }
130
131        let mut final_states = vec![false; state_count];
132        for state in self.final_states {
133            if state >= state_count {
134                return Err(FastStateMachineBuildError::FinalStateOutOfRange { state, state_count });
135            }
136            final_states[state] = true;
137        }
138
139        let mut transitions = vec![usize::MAX; transition_count];
140        for (source, event, target) in self.transitions {
141            if source >= state_count {
142                return Err(FastStateMachineBuildError::TransitionSourceOutOfRange {
143                    source_state: source,
144                    state_count,
145                });
146            }
147            if event >= event_count {
148                return Err(FastStateMachineBuildError::TransitionEventOutOfRange { event, event_count });
149            }
150            if target >= state_count {
151                return Err(FastStateMachineBuildError::TransitionTargetOutOfRange { target, state_count });
152            }
153
154            let index = source
155                .checked_mul(event_count)
156                .and_then(|base| base.checked_add(event))
157                .expect("validated transition index must fit usize");
158
159            match transitions[index] {
160                usize::MAX => transitions[index] = target,
161                existing if existing == target => {}
162                existing => {
163                    return Err(FastStateMachineBuildError::DuplicateTransition {
164                        source_state: source,
165                        event,
166                        existing_target: existing,
167                        new_target: target,
168                    });
169                }
170            }
171        }
172
173        Ok(FastStateMachine {
174            state_count,
175            event_count,
176            initial_states,
177            final_states,
178            transitions,
179            cas: FastCas::with_policy(self.cas_policy),
180        })
181    }
182}
183
184impl Default for FastStateMachineBuilder {
185    fn default() -> Self {
186        Self::new()
187    }
188}