morpheus_network/state/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
//! State machine module.
//!
//! Non-blocking state machines for all network operations.
//! Each state machine has a `step()` method that returns immediately.
//!
//! # Design Principles
//!
//! 1. **No blocking**: `step()` returns immediately, never waits
//! 2. **Timeout as observation**: Check elapsed time, don't spin
//! 3. **State transitions**: Move to next state when condition met
//! 4. **Composable**: State machines can contain other state machines
//!
//! # Usage Pattern
//!
//! ```ignore
//! loop {
//! // Poll network stack (exactly once per iteration)
//! iface.poll(now, device, sockets);
//!
//! // Step state machine (returns immediately)
//! match state_machine.step(now_tsc, &timeouts) {
//! StepResult::Pending => continue,
//! StepResult::Done => break,
//! StepResult::Timeout => handle_timeout(),
//! StepResult::Failed => handle_error(),
//! }
//! }
//! ```
//!
//! # Reference
//! NETWORK_IMPL_GUIDE.md §5
pub mod dhcp;
pub mod disk_writer;
pub mod dns;
pub mod download;
pub mod http;
pub mod tcp;
use crate::boot::TimeoutConfig;
use core::fmt;
// ═══════════════════════════════════════════════════════════════════════════
// STEP RESULT
// ═══════════════════════════════════════════════════════════════════════════
/// Result of a state machine step.
///
/// Each call to `step()` returns one of these values to indicate
/// the current state of the operation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StepResult {
/// Operation in progress, call `step()` again next iteration.
Pending,
/// Operation completed successfully.
/// Call `output()` to get the result.
Done,
/// Operation timed out.
/// Call `error()` for details.
Timeout,
/// Operation failed.
/// Call `error()` for details.
Failed,
}
impl StepResult {
/// Check if operation is still in progress.
#[inline]
pub fn is_pending(self) -> bool {
self == Self::Pending
}
/// Check if operation completed successfully.
#[inline]
pub fn is_done(self) -> bool {
self == Self::Done
}
/// Check if operation terminated (done, timeout, or failed).
#[inline]
pub fn is_terminal(self) -> bool {
!self.is_pending()
}
/// Check if operation ended in error (timeout or failed).
#[inline]
pub fn is_error(self) -> bool {
matches!(self, Self::Timeout | Self::Failed)
}
}
// ═══════════════════════════════════════════════════════════════════════════
// STATE MACHINE ERROR
// ═══════════════════════════════════════════════════════════════════════════
/// Common error type for state machines.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StateError {
/// Operation timed out
Timeout,
/// Network interface error
InterfaceError,
/// Socket error
SocketError,
/// DNS resolution failed
DnsError,
/// TCP connection failed
ConnectionFailed,
/// TCP connection refused
ConnectionRefused,
/// TCP connection reset
ConnectionReset,
/// HTTP protocol error
HttpError,
/// Invalid response
InvalidResponse,
/// HTTP status error (status code)
HttpStatus(u16),
/// Not started (step called before start)
NotStarted,
/// Already completed
AlreadyComplete,
/// Buffer too small
BufferTooSmall,
/// Internal error
Internal,
}
impl fmt::Display for StateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Timeout => write!(f, "operation timed out"),
Self::InterfaceError => write!(f, "network interface error"),
Self::SocketError => write!(f, "socket error"),
Self::DnsError => write!(f, "DNS resolution failed"),
Self::ConnectionFailed => write!(f, "TCP connection failed"),
Self::ConnectionRefused => write!(f, "connection refused"),
Self::ConnectionReset => write!(f, "connection reset"),
Self::HttpError => write!(f, "HTTP protocol error"),
Self::InvalidResponse => write!(f, "invalid response"),
Self::HttpStatus(code) => write!(f, "HTTP status {}", code),
Self::NotStarted => write!(f, "operation not started"),
Self::AlreadyComplete => write!(f, "operation already complete"),
Self::BufferTooSmall => write!(f, "buffer too small"),
Self::Internal => write!(f, "internal error"),
}
}
}
// ═══════════════════════════════════════════════════════════════════════════
// TIMESTAMP WRAPPER
// ═══════════════════════════════════════════════════════════════════════════
/// TSC timestamp wrapper for timeout calculations.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct TscTimestamp(u64);
impl TscTimestamp {
/// Create from raw TSC value.
#[inline]
pub const fn new(tsc: u64) -> Self {
Self(tsc)
}
/// Get raw TSC value.
#[inline]
pub const fn raw(self) -> u64 {
self.0
}
/// Calculate elapsed ticks since this timestamp.
/// Uses wrapping subtraction to handle overflow.
#[inline]
pub fn elapsed(self, now: u64) -> u64 {
now.wrapping_sub(self.0)
}
/// Check if timeout has elapsed.
#[inline]
pub fn is_expired(self, now: u64, timeout_ticks: u64) -> bool {
self.elapsed(now) > timeout_ticks
}
}
impl From<u64> for TscTimestamp {
fn from(tsc: u64) -> Self {
Self(tsc)
}
}
// ═══════════════════════════════════════════════════════════════════════════
// STATE MACHINE CONTEXT
// ═══════════════════════════════════════════════════════════════════════════
/// Context passed to state machine step functions.
///
/// Contains timing information and configuration needed by all state machines.
#[derive(Debug, Clone, Copy)]
pub struct StepContext {
/// Current TSC value
pub now_tsc: u64,
/// Timeout configuration
pub timeouts: TimeoutConfig,
}
impl StepContext {
/// Create new context.
pub fn new(now_tsc: u64, tsc_freq: u64) -> Self {
Self {
now_tsc,
timeouts: TimeoutConfig::new(tsc_freq),
}
}
/// Create timestamp for current time.
#[inline]
pub fn now(&self) -> TscTimestamp {
TscTimestamp(self.now_tsc)
}
/// Check if timeout has elapsed since start.
#[inline]
pub fn is_expired(&self, start: TscTimestamp, timeout_ticks: u64) -> bool {
start.is_expired(self.now_tsc, timeout_ticks)
}
}
// ═══════════════════════════════════════════════════════════════════════════
// PROGRESS TRACKING
// ═══════════════════════════════════════════════════════════════════════════
/// Progress information for long-running operations.
#[derive(Debug, Clone, Copy, Default)]
pub struct Progress {
/// Bytes completed
pub bytes_done: u64,
/// Total bytes (0 if unknown)
pub bytes_total: u64,
/// Start timestamp
pub start_tsc: u64,
/// Last update timestamp
pub last_update_tsc: u64,
}
impl Progress {
/// Create new progress tracker.
pub fn new(now_tsc: u64) -> Self {
Self {
bytes_done: 0,
bytes_total: 0,
start_tsc: now_tsc,
last_update_tsc: now_tsc,
}
}
/// Update progress.
pub fn update(&mut self, bytes_done: u64, now_tsc: u64) {
self.bytes_done = bytes_done;
self.last_update_tsc = now_tsc;
}
/// Set total bytes (when known).
pub fn set_total(&mut self, total: u64) {
self.bytes_total = total;
}
/// Get percentage complete (0-100), or 0 if total unknown.
pub fn percentage(&self) -> u8 {
if self.bytes_total == 0 {
0
} else {
((self.bytes_done * 100) / self.bytes_total) as u8
}
}
/// Calculate bytes per second (approximate).
pub fn bytes_per_second(&self, now_tsc: u64, tsc_freq: u64) -> u64 {
let elapsed = now_tsc.wrapping_sub(self.start_tsc);
if elapsed == 0 {
return 0;
}
// bytes_done * tsc_freq / elapsed
// Avoid overflow by dividing first if needed
if self.bytes_done > u64::MAX / tsc_freq {
(self.bytes_done / elapsed) * tsc_freq
} else {
(self.bytes_done * tsc_freq) / elapsed
}
}
}
// ═══════════════════════════════════════════════════════════════════════════
// RE-EXPORTS
// ═══════════════════════════════════════════════════════════════════════════
pub use dhcp::DhcpState;
pub use dns::DnsResolveState;
pub use download::IsoDownloadState;
pub use http::HttpDownloadState;
pub use tcp::TcpConnState;