1use std::sync::LazyLock;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum InputState {
8 Ground,
10 EscEnter,
12 EscIntermediate,
14 CsiEnter,
16 CsiParameter,
18 CsiIntermediate,
20 CsiIgnore,
22 DcsEnter,
24 DcsParameter,
26 DcsIntermediate,
28 DcsHandler,
30 DcsEscape,
32 DcsIgnore,
34 OscString,
36 ApcString,
38 RenameString,
40 ConsumeSt,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub(crate) enum Handler {
47 None,
48 Print,
49 C0Dispatch,
50 EscDispatch,
51 CsiDispatch,
52 DcsDispatch,
53 Intermediate,
54 Parameter,
55 Input,
56 TopBitSet,
57 EndBel,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub(crate) struct TransitionEntry {
63 pub first: u8,
64 pub last: u8,
65 pub handler: Handler,
66 pub next_state: Option<InputState>,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub(crate) struct Transition {
72 pub handler: Handler,
73 pub next_state: Option<InputState>,
74}
75
76impl InputState {
77 pub(crate) const COUNT: usize = 17;
78
79 pub(crate) const ALL: [Self; Self::COUNT] = [
80 Self::Ground,
81 Self::EscEnter,
82 Self::EscIntermediate,
83 Self::CsiEnter,
84 Self::CsiParameter,
85 Self::CsiIntermediate,
86 Self::CsiIgnore,
87 Self::DcsEnter,
88 Self::DcsParameter,
89 Self::DcsIntermediate,
90 Self::DcsHandler,
91 Self::DcsEscape,
92 Self::DcsIgnore,
93 Self::OscString,
94 Self::ApcString,
95 Self::RenameString,
96 Self::ConsumeSt,
97 ];
98
99 pub(crate) fn transition_table(self) -> &'static [TransitionEntry] {
101 match self {
102 Self::Ground => GROUND_TABLE,
103 Self::EscEnter => ESC_ENTER_TABLE,
104 Self::EscIntermediate => ESC_INTERMEDIATE_TABLE,
105 Self::CsiEnter => CSI_ENTER_TABLE,
106 Self::CsiParameter => CSI_PARAMETER_TABLE,
107 Self::CsiIntermediate => CSI_INTERMEDIATE_TABLE,
108 Self::CsiIgnore => CSI_IGNORE_TABLE,
109 Self::DcsEnter => DCS_ENTER_TABLE,
110 Self::DcsParameter => DCS_PARAMETER_TABLE,
111 Self::DcsIntermediate => DCS_INTERMEDIATE_TABLE,
112 Self::DcsHandler => DCS_HANDLER_TABLE,
113 Self::DcsEscape => DCS_ESCAPE_TABLE,
114 Self::DcsIgnore => DCS_IGNORE_TABLE,
115 Self::OscString => OSC_STRING_TABLE,
116 Self::ApcString => APC_STRING_TABLE,
117 Self::RenameString => RENAME_STRING_TABLE,
118 Self::ConsumeSt => CONSUME_ST_TABLE,
119 }
120 }
121
122 pub(crate) fn transition_for_byte(self, byte: u8) -> Transition {
123 TRANSITION_LUT[self.index()][byte as usize]
124 }
125
126 const fn index(self) -> usize {
127 match self {
128 Self::Ground => 0,
129 Self::EscEnter => 1,
130 Self::EscIntermediate => 2,
131 Self::CsiEnter => 3,
132 Self::CsiParameter => 4,
133 Self::CsiIntermediate => 5,
134 Self::CsiIgnore => 6,
135 Self::DcsEnter => 7,
136 Self::DcsParameter => 8,
137 Self::DcsIntermediate => 9,
138 Self::DcsHandler => 10,
139 Self::DcsEscape => 11,
140 Self::DcsIgnore => 12,
141 Self::OscString => 13,
142 Self::ApcString => 14,
143 Self::RenameString => 15,
144 Self::ConsumeSt => 16,
145 }
146 }
147}
148
149static TRANSITION_LUT: LazyLock<[[Transition; 256]; InputState::COUNT]> =
150 LazyLock::new(build_transition_lut);
151
152fn build_transition_lut() -> [[Transition; 256]; InputState::COUNT] {
153 let fallback = Transition {
154 handler: Handler::None,
155 next_state: None,
156 };
157 let mut lut = [[fallback; 256]; InputState::COUNT];
158
159 for state in InputState::ALL {
160 let row = &mut lut[state.index()];
161 for entry in state.transition_table() {
162 for byte in entry.first..=entry.last {
163 row[byte as usize] = Transition {
164 handler: entry.handler,
165 next_state: entry.next_state,
166 };
167 }
168 }
169 }
170
171 lut
172}
173
174use Handler as H;
176use InputState as S;
177
178const fn e(
179 first: u8,
180 last: u8,
181 handler: Handler,
182 next_state: Option<InputState>,
183) -> TransitionEntry {
184 TransitionEntry {
185 first,
186 last,
187 handler,
188 next_state,
189 }
190}
191
192static GROUND_TABLE: &[TransitionEntry] = &[
194 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
195 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
196 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
197 e(0x00, 0x17, H::C0Dispatch, None),
198 e(0x19, 0x19, H::C0Dispatch, None),
199 e(0x1c, 0x1f, H::C0Dispatch, None),
200 e(0x20, 0x7e, H::Print, None),
201 e(0x7f, 0x7f, H::None, None),
202 e(0x80, 0xff, H::TopBitSet, None),
203];
204
205static ESC_ENTER_TABLE: &[TransitionEntry] = &[
207 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
208 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
209 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
210 e(0x00, 0x17, H::C0Dispatch, None),
211 e(0x19, 0x19, H::C0Dispatch, None),
212 e(0x1c, 0x1f, H::C0Dispatch, None),
213 e(0x20, 0x2f, H::Intermediate, Some(S::EscIntermediate)),
214 e(0x30, 0x4f, H::EscDispatch, Some(S::Ground)),
215 e(0x50, 0x50, H::None, Some(S::DcsEnter)),
216 e(0x51, 0x57, H::EscDispatch, Some(S::Ground)),
217 e(0x58, 0x58, H::None, Some(S::ConsumeSt)),
218 e(0x59, 0x5a, H::EscDispatch, Some(S::Ground)),
219 e(0x5b, 0x5b, H::None, Some(S::CsiEnter)),
220 e(0x5c, 0x5c, H::EscDispatch, Some(S::Ground)),
221 e(0x5d, 0x5d, H::None, Some(S::OscString)),
222 e(0x5e, 0x5e, H::None, Some(S::ConsumeSt)),
223 e(0x5f, 0x5f, H::None, Some(S::ApcString)),
224 e(0x60, 0x6a, H::EscDispatch, Some(S::Ground)),
225 e(0x6b, 0x6b, H::None, Some(S::RenameString)),
226 e(0x6c, 0x7e, H::EscDispatch, Some(S::Ground)),
227 e(0x7f, 0xff, H::None, None),
228];
229
230static ESC_INTERMEDIATE_TABLE: &[TransitionEntry] = &[
232 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
233 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
234 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
235 e(0x00, 0x17, H::C0Dispatch, None),
236 e(0x19, 0x19, H::C0Dispatch, None),
237 e(0x1c, 0x1f, H::C0Dispatch, None),
238 e(0x20, 0x2f, H::Intermediate, None),
239 e(0x30, 0x7e, H::EscDispatch, Some(S::Ground)),
240 e(0x7f, 0xff, H::None, None),
241];
242
243static CSI_ENTER_TABLE: &[TransitionEntry] = &[
245 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
246 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
247 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
248 e(0x00, 0x17, H::C0Dispatch, None),
249 e(0x19, 0x19, H::C0Dispatch, None),
250 e(0x1c, 0x1f, H::C0Dispatch, None),
251 e(0x20, 0x2f, H::Intermediate, Some(S::CsiIntermediate)),
252 e(0x30, 0x39, H::Parameter, Some(S::CsiParameter)),
253 e(0x3a, 0x3a, H::Parameter, Some(S::CsiParameter)),
254 e(0x3b, 0x3b, H::Parameter, Some(S::CsiParameter)),
255 e(0x3c, 0x3f, H::Intermediate, Some(S::CsiParameter)),
256 e(0x40, 0x7e, H::CsiDispatch, Some(S::Ground)),
257 e(0x7f, 0xff, H::None, None),
258];
259
260static CSI_PARAMETER_TABLE: &[TransitionEntry] = &[
262 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
263 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
264 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
265 e(0x00, 0x17, H::C0Dispatch, None),
266 e(0x19, 0x19, H::C0Dispatch, None),
267 e(0x1c, 0x1f, H::C0Dispatch, None),
268 e(0x20, 0x2f, H::Intermediate, Some(S::CsiIntermediate)),
269 e(0x30, 0x39, H::Parameter, None),
270 e(0x3a, 0x3a, H::Parameter, None),
271 e(0x3b, 0x3b, H::Parameter, None),
272 e(0x3c, 0x3f, H::None, Some(S::CsiIgnore)),
273 e(0x40, 0x7e, H::CsiDispatch, Some(S::Ground)),
274 e(0x7f, 0xff, H::None, None),
275];
276
277static CSI_INTERMEDIATE_TABLE: &[TransitionEntry] = &[
279 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
280 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
281 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
282 e(0x00, 0x17, H::C0Dispatch, None),
283 e(0x19, 0x19, H::C0Dispatch, None),
284 e(0x1c, 0x1f, H::C0Dispatch, None),
285 e(0x20, 0x2f, H::Intermediate, None),
286 e(0x30, 0x3f, H::None, Some(S::CsiIgnore)),
287 e(0x40, 0x7e, H::CsiDispatch, Some(S::Ground)),
288 e(0x7f, 0xff, H::None, None),
289];
290
291static CSI_IGNORE_TABLE: &[TransitionEntry] = &[
293 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
294 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
295 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
296 e(0x00, 0x17, H::C0Dispatch, None),
297 e(0x19, 0x19, H::C0Dispatch, None),
298 e(0x1c, 0x1f, H::C0Dispatch, None),
299 e(0x20, 0x3f, H::None, None),
300 e(0x40, 0x7e, H::None, Some(S::Ground)),
301 e(0x7f, 0xff, H::None, None),
302];
303
304static DCS_ENTER_TABLE: &[TransitionEntry] = &[
306 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
307 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
308 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
309 e(0x00, 0x17, H::None, None),
310 e(0x19, 0x19, H::None, None),
311 e(0x1c, 0x1f, H::None, None),
312 e(0x20, 0x2f, H::Intermediate, Some(S::DcsIntermediate)),
313 e(0x30, 0x39, H::Parameter, Some(S::DcsParameter)),
314 e(0x3a, 0x3a, H::None, Some(S::DcsIgnore)),
315 e(0x3b, 0x3b, H::Parameter, Some(S::DcsParameter)),
316 e(0x3c, 0x3f, H::Intermediate, Some(S::DcsParameter)),
317 e(0x40, 0x7e, H::Input, Some(S::DcsHandler)),
318 e(0x7f, 0xff, H::None, None),
319];
320
321static DCS_PARAMETER_TABLE: &[TransitionEntry] = &[
323 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
324 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
325 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
326 e(0x00, 0x17, H::None, None),
327 e(0x19, 0x19, H::None, None),
328 e(0x1c, 0x1f, H::None, None),
329 e(0x20, 0x2f, H::Intermediate, Some(S::DcsIntermediate)),
330 e(0x30, 0x39, H::Parameter, None),
331 e(0x3a, 0x3a, H::None, Some(S::DcsIgnore)),
332 e(0x3b, 0x3b, H::Parameter, None),
333 e(0x3c, 0x3f, H::None, Some(S::DcsIgnore)),
334 e(0x40, 0x7e, H::Input, Some(S::DcsHandler)),
335 e(0x7f, 0xff, H::None, None),
336];
337
338static DCS_INTERMEDIATE_TABLE: &[TransitionEntry] = &[
340 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
341 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
342 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
343 e(0x00, 0x17, H::None, None),
344 e(0x19, 0x19, H::None, None),
345 e(0x1c, 0x1f, H::None, None),
346 e(0x20, 0x2f, H::Intermediate, None),
347 e(0x30, 0x3f, H::None, Some(S::DcsIgnore)),
348 e(0x40, 0x7e, H::Input, Some(S::DcsHandler)),
349 e(0x7f, 0xff, H::None, None),
350];
351
352static DCS_HANDLER_TABLE: &[TransitionEntry] = &[
355 e(0x00, 0x1a, H::Input, None),
356 e(0x1b, 0x1b, H::None, Some(S::DcsEscape)),
357 e(0x1c, 0xff, H::Input, None),
358];
359
360static DCS_ESCAPE_TABLE: &[TransitionEntry] = &[
363 e(0x00, 0x5b, H::Input, Some(S::DcsHandler)),
364 e(0x5c, 0x5c, H::DcsDispatch, Some(S::Ground)),
365 e(0x5d, 0xff, H::Input, Some(S::DcsHandler)),
366];
367
368static DCS_IGNORE_TABLE: &[TransitionEntry] = &[
370 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
371 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
372 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
373 e(0x00, 0x17, H::None, None),
374 e(0x19, 0x19, H::None, None),
375 e(0x1c, 0x1f, H::None, None),
376 e(0x20, 0xff, H::None, None),
377];
378
379static OSC_STRING_TABLE: &[TransitionEntry] = &[
381 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
382 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
383 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
384 e(0x00, 0x06, H::None, None),
385 e(0x07, 0x07, H::EndBel, Some(S::Ground)),
386 e(0x08, 0x17, H::None, None),
387 e(0x19, 0x19, H::None, None),
388 e(0x1c, 0x1f, H::None, None),
389 e(0x20, 0xff, H::Input, None),
390];
391
392static APC_STRING_TABLE: &[TransitionEntry] = &[
394 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
395 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
396 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
397 e(0x00, 0x17, H::None, None),
398 e(0x19, 0x19, H::None, None),
399 e(0x1c, 0x1f, H::None, None),
400 e(0x20, 0xff, H::Input, None),
401];
402
403static RENAME_STRING_TABLE: &[TransitionEntry] = &[
405 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
406 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
407 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
408 e(0x00, 0x17, H::None, None),
409 e(0x19, 0x19, H::None, None),
410 e(0x1c, 0x1f, H::None, None),
411 e(0x20, 0xff, H::Input, None),
412];
413
414static CONSUME_ST_TABLE: &[TransitionEntry] = &[
416 e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
417 e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
418 e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
419 e(0x00, 0x17, H::None, None),
420 e(0x19, 0x19, H::None, None),
421 e(0x1c, 0x1f, H::None, None),
422 e(0x20, 0xff, H::None, None),
423];