1use serde_json::Value;
11
12use crate::wire::{Event, Reply};
13
14pub const MAX_MESSAGE: usize = 32 << 20;
16
17const PASTE_START: &[u8] = b"\x1b[200~";
19const PASTE_END: &[u8] = b"\x1b[201~";
21const APC_PREFIX: &[u8] = b"tsp;";
23const OSC_PREFIX: &[u8] = b"877;tsp;";
25
26#[derive(Clone, Debug, PartialEq)]
28pub enum Item {
29 Keys(Vec<u8>),
31 Reply(Reply),
33 Event(Event),
35 Da1,
37}
38
39enum Scan {
41 Undecided,
43 Keys(usize),
45 Da1(usize),
47 Paste(usize),
49 Tsp(usize),
51}
52
53#[derive(Clone, Debug, Default)]
65pub struct Parser {
66 buf: Vec<u8>,
68 paste: bool,
70 tsp: Option<usize>,
72 scanned: usize,
74 skipping: bool,
76}
77
78impl Parser {
79 pub fn new() -> Self {
81 Self::default()
82 }
83
84 pub const fn pending(&self) -> bool {
87 self.tsp.is_none() && !self.skipping && !self.paste && !self.buf.is_empty()
88 }
89
90 pub fn feed(&mut self, bytes: &[u8]) -> Vec<Item> {
92 let mut out = Vec::new();
93 self.buf.extend_from_slice(bytes);
94 self.run(&mut out);
95 out
96 }
97
98 pub fn flush(&mut self) -> Vec<Item> {
101 if self.pending() {
102 vec![Item::Keys(std::mem::take(&mut self.buf))]
103 } else {
104 Vec::new()
105 }
106 }
107
108 fn run(&mut self, out: &mut Vec<Item>) {
110 let mut keys: Vec<u8> = Vec::new();
111 let mut at = 0;
112 loop {
113 if self.skipping || self.tsp.is_some() {
114 match self.message(at, out, &mut keys) {
115 Some(next) => at = next,
116 None => break,
117 }
118 continue;
119 }
120 if self.paste {
121 let rest = &self.buf[at..];
122 if let Some(end) = find(rest, PASTE_END) {
123 keys.extend_from_slice(&rest[..end + PASTE_END.len()]);
124 at += end + PASTE_END.len();
125 self.paste = false;
126 continue;
127 }
128 let hold = partial_suffix(rest, PASTE_END);
129 keys.extend_from_slice(&rest[..rest.len() - hold]);
130 at = self.buf.len() - hold;
131 break;
132 }
133 let rest = &self.buf[at..];
134 if rest.is_empty() {
135 break;
136 }
137 let plain = rest.iter().position(|&b| b == 0x1b).unwrap_or(rest.len());
138 if plain > 0 {
139 keys.extend_from_slice(&rest[..plain]);
140 at += plain;
141 continue;
142 }
143 match scan(rest) {
144 Scan::Undecided => break,
145 Scan::Keys(n) => {
146 keys.extend_from_slice(&rest[..n]);
147 at += n;
148 },
149 Scan::Paste(n) => {
150 keys.extend_from_slice(&rest[..n]);
151 at += n;
152 self.paste = true;
153 },
154 Scan::Da1(n) => {
155 push_keys(out, &mut keys);
156 out.push(Item::Da1);
157 at += n;
158 },
159 Scan::Tsp(content) => {
160 self.buf.drain(..at);
162 self.tsp = Some(content);
163 self.scanned = content;
164 at = 0;
165 },
166 }
167 }
168 push_keys(out, &mut keys);
169 self.buf.drain(..at);
170 }
171
172 fn message(&mut self, at: usize, out: &mut Vec<Item>, keys: &mut Vec<u8>) -> Option<usize> {
176 debug_assert_eq!(at, 0, "a TSP message is rebased to the buffer start");
177 let from = self.scanned;
178 let Some((end, term_len)) = terminator(&self.buf[from..]).map(|(i, n)| (from + i, n)) else {
179 self.scanned = self.buf.len().saturating_sub(1).max(from);
181 if !self.skipping && self.buf.len() > MAX_MESSAGE {
182 tracing::warn!(bytes = self.buf.len(), "dropping an oversized TSP message");
183 self.skipping = true;
184 self.tsp = None;
185 }
186 if self.skipping {
187 let keep = usize::from(self.buf.last() == Some(&0x1b));
188 let len = self.buf.len();
189 self.buf.drain(..len - keep);
190 self.scanned = 0;
191 }
192 return None;
193 };
194 if !self.skipping
195 && let Some(content) = self.tsp
196 {
197 push_keys(out, keys);
198 if let Some(item) = decode(&self.buf[content..end]) {
199 out.push(item);
200 }
201 }
202 self.skipping = false;
203 self.tsp = None;
204 self.scanned = 0;
205 self.buf.drain(..end + term_len);
206 Some(0)
207 }
208}
209
210fn push_keys(out: &mut Vec<Item>, keys: &mut Vec<u8>) {
212 if !keys.is_empty() {
213 out.push(Item::Keys(std::mem::take(keys)));
214 }
215}
216
217fn find(hay: &[u8], needle: &[u8]) -> Option<usize> {
219 hay.windows(needle.len()).position(|w| w == needle)
220}
221
222fn partial_suffix(hay: &[u8], needle: &[u8]) -> usize {
224 (1..needle.len())
225 .rev()
226 .find(|&n| hay.ends_with(&needle[..n]))
227 .unwrap_or(0)
228}
229
230fn terminator(bytes: &[u8]) -> Option<(usize, usize)> {
232 let mut i = 0;
233 while i < bytes.len() {
234 match bytes[i] {
235 0x07 => return Some((i, 1)),
236 0x1b if bytes.get(i + 1) == Some(&b'\\') => return Some((i, 2)),
237 _ => {},
238 }
239 i += 1;
240 }
241 None
242}
243
244fn scan(rest: &[u8]) -> Scan {
247 let Some(&second) = rest.get(1) else {
248 return Scan::Undecided;
249 };
250 match second {
251 b'_' => prefixed(rest, APC_PREFIX),
252 b']' => prefixed(rest, OSC_PREFIX),
253 b'[' => csi(rest),
254 _ => Scan::Keys(1),
256 }
257}
258
259fn prefixed(rest: &[u8], prefix: &[u8]) -> Scan {
261 let after = &rest[2..];
262 let n = after.len().min(prefix.len());
263 if after[..n] != prefix[..n] {
264 return Scan::Keys(2);
265 }
266 if n < prefix.len() {
267 return Scan::Undecided;
268 }
269 Scan::Tsp(2 + prefix.len())
270}
271
272fn csi(rest: &[u8]) -> Scan {
274 let mut i = 2;
275 while let Some(&b) = rest.get(i) {
276 match b {
277 0x20..=0x3f => i += 1,
278 0x40..=0x7e => {
279 let seq = &rest[..=i];
280 let params = &seq[2..i];
281 if b == b'c'
282 && params.first() == Some(&b'?')
283 && params[1..].iter().all(|&p| p.is_ascii_digit() || p == b';')
284 {
285 return Scan::Da1(i + 1);
286 }
287 if seq == PASTE_START {
288 return Scan::Paste(i + 1);
289 }
290 return Scan::Keys(i + 1);
291 },
292 _ => return Scan::Keys(i),
294 }
295 }
296 Scan::Undecided
297}
298
299fn decode(content: &[u8]) -> Option<Item> {
302 let semi = content.iter().position(|&b| b == b';')?;
303 let verb = &content[..semi];
304 let mut pos = semi + 1;
305 while let Some(len) = content[pos..].iter().position(|&b| b == b';') {
306 if !parameter(&content[pos..pos + len]) {
307 break;
308 }
309 pos += len + 1;
310 }
311 let body = &content[pos..];
312 let Ok(Value::Object(map)) = serde_json::from_slice::<Value>(body) else {
313 tracing::debug!("dropping a TSP message whose body is not a JSON object");
314 return None;
315 };
316 match verb {
317 b"r" => Some(Item::Reply(Reply::decode(map))),
318 b"e" => Some(Item::Event(Event::decode(map))),
319 _ => None,
320 }
321}
322
323fn parameter(segment: &[u8]) -> bool {
325 let Some(eq) = segment.iter().position(|&b| b == b'=') else {
326 return false;
327 };
328 let (key, value) = (&segment[..eq], &segment[eq + 1..]);
329 !key.is_empty()
330 && key
331 .iter()
332 .all(|&b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-')
333 && value
334 .iter()
335 .all(|&b| (0x21..=0x7e).contains(&b) && b != b';')
336}