1#![cfg_attr(not(test), no_std)]
32#![cfg_attr(docsrs, feature(doc_auto_cfg))]
33#![allow(missing_docs)]
34#![warn(clippy::print_stderr)]
35#![warn(clippy::print_stdout)]
36
37#[cfg(not(feature = "core"))]
38extern crate alloc;
39
40use core::mem::MaybeUninit;
41
42#[cfg(feature = "core")]
43use arrayvec::ArrayVec;
44#[cfg(feature = "utf8")]
45use utf8parse as utf8;
46
47mod params;
48pub mod state;
49
50pub use params::{Params, ParamsIter};
51
52use state::{state_change, Action, State};
53
54const MAX_INTERMEDIATES: usize = 2;
55const MAX_OSC_PARAMS: usize = 16;
56#[cfg(feature = "core")]
57const MAX_OSC_RAW: usize = 1024;
58
59#[allow(unused_qualifications)]
61#[derive(Default, Clone, Debug, PartialEq, Eq)]
62pub struct Parser<C = DefaultCharAccumulator> {
63    state: State,
64    intermediates: [u8; MAX_INTERMEDIATES],
65    intermediate_idx: usize,
66    params: Params,
67    param: u16,
68    #[cfg(feature = "core")]
69    osc_raw: ArrayVec<u8, MAX_OSC_RAW>,
70    #[cfg(not(feature = "core"))]
71    osc_raw: alloc::vec::Vec<u8>,
72    osc_params: [(usize, usize); MAX_OSC_PARAMS],
73    osc_num_params: usize,
74    ignoring: bool,
75    utf8_parser: C,
76}
77
78impl<C> Parser<C>
79where
80    C: CharAccumulator,
81{
82    pub fn new() -> Parser {
84        Parser::default()
85    }
86
87    #[inline]
88    fn params(&self) -> &Params {
89        &self.params
90    }
91
92    #[inline]
93    fn intermediates(&self) -> &[u8] {
94        &self.intermediates[..self.intermediate_idx]
95    }
96
97    #[inline]
101    pub fn advance<P: Perform>(&mut self, performer: &mut P, byte: u8) {
102        if let State::Utf8 = self.state {
104            self.process_utf8(performer, byte);
105            return;
106        }
107
108        let (state, action) = state_change(self.state, byte);
109        self.perform_state_change(performer, state, action, byte);
110    }
111
112    #[inline]
113    fn process_utf8<P>(&mut self, performer: &mut P, byte: u8)
114    where
115        P: Perform,
116    {
117        if let Some(c) = self.utf8_parser.add(byte) {
118            performer.print(c);
119            self.state = State::Ground;
120        }
121    }
122
123    #[inline]
124    fn perform_state_change<P>(&mut self, performer: &mut P, state: State, action: Action, byte: u8)
125    where
126        P: Perform,
127    {
128        match state {
129            State::Anywhere => {
130                self.perform_action(performer, action, byte);
132            }
133            state => {
134                match self.state {
135                    State::DcsPassthrough => {
136                        self.perform_action(performer, Action::Unhook, byte);
137                    }
138                    State::OscString => {
139                        self.perform_action(performer, Action::OscEnd, byte);
140                    }
141                    _ => (),
142                }
143
144                match action {
145                    Action::Nop => (),
146                    action => {
147                        self.perform_action(performer, action, byte);
148                    }
149                }
150
151                match state {
152                    State::CsiEntry | State::DcsEntry | State::Escape => {
153                        self.perform_action(performer, Action::Clear, byte);
154                    }
155                    State::DcsPassthrough => {
156                        self.perform_action(performer, Action::Hook, byte);
157                    }
158                    State::OscString => {
159                        self.perform_action(performer, Action::OscStart, byte);
160                    }
161                    _ => (),
162                }
163
164                self.state = state;
166            }
167        }
168    }
169
170    #[inline]
174    fn osc_dispatch<P: Perform>(&self, performer: &mut P, byte: u8) {
175        let mut slices: [MaybeUninit<&[u8]>; MAX_OSC_PARAMS] =
176            unsafe { MaybeUninit::uninit().assume_init() };
177
178        for (i, slice) in slices.iter_mut().enumerate().take(self.osc_num_params) {
179            let indices = self.osc_params[i];
180            *slice = MaybeUninit::new(&self.osc_raw[indices.0..indices.1]);
181        }
182
183        unsafe {
184            let num_params = self.osc_num_params;
185            let params = &slices[..num_params] as *const [MaybeUninit<&[u8]>] as *const [&[u8]];
186            performer.osc_dispatch(&*params, byte == 0x07);
187        }
188    }
189
190    #[inline]
191    fn perform_action<P: Perform>(&mut self, performer: &mut P, action: Action, byte: u8) {
192        match action {
193            Action::Print => performer.print(byte as char),
194            Action::Execute => performer.execute(byte),
195            Action::Hook => {
196                if self.params.is_full() {
197                    self.ignoring = true;
198                } else {
199                    self.params.push(self.param);
200                }
201
202                performer.hook(self.params(), self.intermediates(), self.ignoring, byte);
203            }
204            Action::Put => performer.put(byte),
205            Action::OscStart => {
206                self.osc_raw.clear();
207                self.osc_num_params = 0;
208            }
209            Action::OscPut => {
210                #[cfg(feature = "core")]
211                {
212                    if self.osc_raw.is_full() {
213                        return;
214                    }
215                }
216
217                let idx = self.osc_raw.len();
218
219                if byte == b';' {
221                    let param_idx = self.osc_num_params;
222                    match param_idx {
223                        MAX_OSC_PARAMS => return,
225
226                        0 => {
228                            self.osc_params[param_idx] = (0, idx);
229                        }
230
231                        _ => {
233                            let prev = self.osc_params[param_idx - 1];
234                            let begin = prev.1;
235                            self.osc_params[param_idx] = (begin, idx);
236                        }
237                    }
238
239                    self.osc_num_params += 1;
240                } else {
241                    self.osc_raw.push(byte);
242                }
243            }
244            Action::OscEnd => {
245                let param_idx = self.osc_num_params;
246                let idx = self.osc_raw.len();
247
248                match param_idx {
249                    MAX_OSC_PARAMS => (),
251
252                    0 => {
254                        self.osc_params[param_idx] = (0, idx);
255                        self.osc_num_params += 1;
256                    }
257
258                    _ => {
260                        let prev = self.osc_params[param_idx - 1];
261                        let begin = prev.1;
262                        self.osc_params[param_idx] = (begin, idx);
263                        self.osc_num_params += 1;
264                    }
265                }
266                self.osc_dispatch(performer, byte);
267            }
268            Action::Unhook => performer.unhook(),
269            Action::CsiDispatch => {
270                if self.params.is_full() {
271                    self.ignoring = true;
272                } else {
273                    self.params.push(self.param);
274                }
275
276                performer.csi_dispatch(self.params(), self.intermediates(), self.ignoring, byte);
277            }
278            Action::EscDispatch => {
279                performer.esc_dispatch(self.intermediates(), self.ignoring, byte);
280            }
281            Action::Collect => {
282                if self.intermediate_idx == MAX_INTERMEDIATES {
283                    self.ignoring = true;
284                } else {
285                    self.intermediates[self.intermediate_idx] = byte;
286                    self.intermediate_idx += 1;
287                }
288            }
289            Action::Param => {
290                if self.params.is_full() {
291                    self.ignoring = true;
292                    return;
293                }
294
295                if byte == b';' {
296                    self.params.push(self.param);
297                    self.param = 0;
298                } else if byte == b':' {
299                    self.params.extend(self.param);
300                    self.param = 0;
301                } else {
302                    self.param = self.param.saturating_mul(10);
304                    self.param = self.param.saturating_add((byte - b'0') as u16);
305                }
306            }
307            Action::Clear => {
308                self.intermediate_idx = 0;
310                self.ignoring = false;
311                self.param = 0;
312
313                self.params.clear();
314            }
315            Action::BeginUtf8 => self.process_utf8(performer, byte),
316            Action::Ignore => (),
317            Action::Nop => (),
318        }
319    }
320}
321
322pub trait CharAccumulator: Default {
324    fn add(&mut self, byte: u8) -> Option<char>;
328}
329
330#[cfg(feature = "utf8")]
332pub type DefaultCharAccumulator = Utf8Parser;
333#[cfg(not(feature = "utf8"))]
334pub type DefaultCharAccumulator = AsciiParser;
335
336#[allow(clippy::exhaustive_structs)]
338#[derive(Default, Clone, Debug, PartialEq, Eq)]
339pub struct AsciiParser;
340
341impl CharAccumulator for AsciiParser {
342    fn add(&mut self, _byte: u8) -> Option<char> {
343        unreachable!("multi-byte UTF8 characters are unsupported")
344    }
345}
346
347#[cfg(feature = "utf8")]
349#[derive(Default, Clone, Debug, PartialEq, Eq)]
350pub struct Utf8Parser {
351    utf8_parser: utf8::Parser,
352}
353
354#[cfg(feature = "utf8")]
355impl CharAccumulator for Utf8Parser {
356    fn add(&mut self, byte: u8) -> Option<char> {
357        let mut c = None;
358        let mut receiver = VtUtf8Receiver(&mut c);
359        self.utf8_parser.advance(&mut receiver, byte);
360        c
361    }
362}
363
364#[cfg(feature = "utf8")]
365struct VtUtf8Receiver<'a>(&'a mut Option<char>);
366
367#[cfg(feature = "utf8")]
368impl utf8::Receiver for VtUtf8Receiver<'_> {
369    fn codepoint(&mut self, c: char) {
370        *self.0 = Some(c);
371    }
372
373    fn invalid_sequence(&mut self) {
374        *self.0 = Some('�');
375    }
376}
377
378pub trait Perform {
389    fn print(&mut self, _c: char) {}
391
392    fn execute(&mut self, _byte: u8) {}
394
395    fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _action: u8) {}
405
406    fn put(&mut self, _byte: u8) {}
409
410    fn unhook(&mut self) {}
415
416    fn osc_dispatch(&mut self, _params: &[&[u8]], _bell_terminated: bool) {}
418
419    fn csi_dispatch(
425        &mut self,
426        _params: &Params,
427        _intermediates: &[u8],
428        _ignore: bool,
429        _action: u8,
430    ) {
431    }
432
433    fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {}
438}
439
440#[doc = include_str!("../README.md")]
441#[cfg(doctest)]
442pub struct ReadmeDoctests;