maelstrom_base/
tty.rs

1use crate::WindowSize;
2use std::mem;
3
4pub fn encode_input(bytes: &[u8]) -> impl Iterator<Item = &[u8]> {
5    bytes
6        .split(|b| *b == 0xff)
7        .enumerate()
8        .flat_map(|(n, bytes)| {
9            let mut vec = Vec::<&[u8]>::new();
10            if n > 0 {
11                vec.push(b"\xff\xff");
12            }
13            if !bytes.is_empty() {
14                vec.push(bytes);
15            }
16            vec
17        })
18}
19
20pub fn encode_window_size_change(window_size: WindowSize) -> [u8; 6] {
21    let rows = window_size.rows.to_be_bytes();
22    let columns = window_size.columns.to_be_bytes();
23    [0xff, 0, rows[0], rows[1], columns[0], columns[1]]
24}
25
26pub trait DecodeInputAcceptor<E> {
27    fn input(&mut self, input: &[u8]) -> Result<(), E>;
28    fn window_size_change(&mut self, window_size: WindowSize) -> Result<(), E>;
29}
30
31#[derive(Debug, Default, PartialEq)]
32pub struct DecodeInputRemainder {
33    data: [u8; 5],
34    length: u8,
35}
36
37impl DecodeInputRemainder {
38    pub fn new(remainder: &[u8]) -> Self {
39        let mut res: Self = Default::default();
40        let length = remainder.len();
41        assert!(length <= mem::size_of_val(&res.data));
42        res.data[..length].copy_from_slice(remainder);
43        res.length = length as u8;
44        res
45    }
46
47    pub fn move_to_slice(&mut self, dest: &mut [u8]) -> usize {
48        let length = self.len();
49        dest[..length].copy_from_slice(&self.data[..length]);
50        *self = Self::default();
51        length
52    }
53
54    pub fn len(&self) -> usize {
55        self.length.into()
56    }
57
58    pub fn is_empty(&self) -> bool {
59        self.len() == 0
60    }
61}
62
63#[derive(Debug, PartialEq)]
64pub enum DecodeInputChunk<'a> {
65    Input(&'a [u8]),
66    WindowSizeChange(WindowSize),
67    Remainder(DecodeInputRemainder),
68}
69
70pub struct DecodeInputIterator<'a>(&'a [u8]);
71
72impl<'a> Iterator for DecodeInputIterator<'a> {
73    type Item = DecodeInputChunk<'a>;
74    fn next(&mut self) -> Option<Self::Item> {
75        match self.0 {
76            [] => None,
77            [0xff, 0xff, rest @ ..] => {
78                let res = Some(DecodeInputChunk::Input(&self.0[1..2]));
79                self.0 = rest;
80                res
81            }
82            [0xff, 0, rh, rl, ch, cl, rest @ ..] => {
83                let res = Some(DecodeInputChunk::WindowSizeChange(WindowSize::new(
84                    u16::from_be_bytes([*rh, *rl]),
85                    u16::from_be_bytes([*ch, *cl]),
86                )));
87                self.0 = rest;
88                res
89            }
90            [0xff, 0, ..] | [0xff] => {
91                let res = Some(DecodeInputChunk::Remainder(DecodeInputRemainder::new(
92                    self.0,
93                )));
94                self.0 = b"";
95                res
96            }
97            [0xff, _, rest @ ..] => {
98                let res = Some(DecodeInputChunk::Input(&self.0[..2]));
99                self.0 = rest;
100                res
101            }
102            _ => {
103                let next_0xff = self
104                    .0
105                    .iter()
106                    .position(|b| *b == 0xff)
107                    .unwrap_or(self.0.len());
108                let res = Some(DecodeInputChunk::Input(&self.0[..next_0xff]));
109                self.0 = &self.0[next_0xff..];
110                res
111            }
112        }
113    }
114}
115
116pub fn decode_input(input: &[u8]) -> DecodeInputIterator {
117    DecodeInputIterator(input)
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use DecodeInputChunk::*;
124
125    fn assert_encode_input<const N: usize>(input: &[u8], expected: [&[u8]; N]) {
126        assert_eq!(
127            Vec::from_iter(encode_input(input)),
128            Vec::from_iter(expected)
129        );
130    }
131
132    #[test]
133    fn encode_input_empty() {
134        assert_encode_input(b"", []);
135    }
136
137    #[test]
138    fn encode_input_basic() {
139        assert_encode_input(b"abc", [b"abc"]);
140    }
141
142    #[test]
143    fn encode_input_leading_escape() {
144        assert_encode_input(b"\xffabcdef", [b"\xff\xff", b"abcdef"]);
145    }
146
147    #[test]
148    fn encode_input_leading_escapes() {
149        assert_encode_input(
150            b"\xff\xff\xffabcdef",
151            [b"\xff\xff", b"\xff\xff", b"\xff\xff", b"abcdef"],
152        );
153    }
154
155    #[test]
156    fn encode_input_only_escape() {
157        assert_encode_input(b"\xff", [b"\xff\xff"]);
158    }
159
160    #[test]
161    fn encode_input_only_escapes() {
162        assert_encode_input(b"\xff\xff\xff", [b"\xff\xff", b"\xff\xff", b"\xff\xff"]);
163    }
164
165    #[test]
166    fn encode_input_trailing_escape() {
167        assert_encode_input(b"abcdef\xff", [b"abcdef", b"\xff\xff"]);
168    }
169
170    #[test]
171    fn encode_input_trailing_escapes() {
172        assert_encode_input(
173            b"abcdef\xff\xff\xff",
174            [b"abcdef", b"\xff\xff", b"\xff\xff", b"\xff\xff"],
175        );
176    }
177
178    #[test]
179    fn encode_input_middle_escape() {
180        assert_encode_input(b"abc\xffdef", [b"abc", b"\xff\xff", b"def"]);
181    }
182
183    #[test]
184    fn encode_input_middle_escapes() {
185        assert_encode_input(
186            b"abc\xff\xff\xffdef",
187            [b"abc", b"\xff\xff", b"\xff\xff", b"\xff\xff", b"def"],
188        );
189    }
190
191    #[test]
192    fn encode_input_escapes() {
193        assert_encode_input(
194            b"\xffabc\xff\xff\xffdef\xff\xff",
195            [
196                b"\xff\xff",
197                b"abc",
198                b"\xff\xff",
199                b"\xff\xff",
200                b"\xff\xff",
201                b"def",
202                b"\xff\xff",
203                b"\xff\xff",
204            ],
205        );
206    }
207
208    #[test]
209    fn encode_window_size_change_basic() {
210        assert_eq!(
211            encode_window_size_change(WindowSize::new(0x89ab, 0xcdef)),
212            *b"\xff\x00\x89\xab\xcd\xef"
213        );
214    }
215
216    fn assert_decode_input<const N: usize>(input: &[u8], expected: [DecodeInputChunk; N]) {
217        assert_eq!(
218            Vec::from_iter(decode_input(input)),
219            Vec::from_iter(expected),
220        );
221    }
222
223    #[test]
224    fn decode_input_empty() {
225        assert_decode_input(b"", []);
226    }
227
228    #[test]
229    fn decode_input_no_escapes() {
230        assert_decode_input(b"abcdef", [Input(b"abcdef")]);
231    }
232
233    #[test]
234    fn decode_input_escape_short_1_last() {
235        assert_decode_input(
236            b"abcdef\xff",
237            [
238                Input(b"abcdef"),
239                Remainder(DecodeInputRemainder::new(b"\xff")),
240            ],
241        );
242    }
243
244    #[test]
245    fn decode_input_escape_window_short_4_last() {
246        assert_decode_input(
247            b"abcdef\xff\x00",
248            [
249                Input(b"abcdef"),
250                Remainder(DecodeInputRemainder::new(b"\xff\x00")),
251            ],
252        );
253    }
254
255    #[test]
256    fn decode_input_escape_window_short_3_last() {
257        assert_decode_input(
258            b"abcdef\xff\x00\x89",
259            [
260                Input(b"abcdef"),
261                Remainder(DecodeInputRemainder::new(b"\xff\x00\x89")),
262            ],
263        );
264    }
265
266    #[test]
267    fn decode_input_escape_window_short_2_last() {
268        assert_decode_input(
269            b"abcdef\xff\x00\x89\xab",
270            [
271                Input(b"abcdef"),
272                Remainder(DecodeInputRemainder::new(b"\xff\x00\x89\xab")),
273            ],
274        );
275    }
276
277    #[test]
278    fn decode_input_escape_window_short_1_last() {
279        assert_decode_input(
280            b"abcdef\xff\x00\x89\xab\xcd",
281            [
282                Input(b"abcdef"),
283                Remainder(DecodeInputRemainder::new(b"\xff\x00\x89\xab\xcd")),
284            ],
285        );
286    }
287
288    #[test]
289    fn decode_input_escape_window_last() {
290        assert_decode_input(
291            b"abcdef\xff\x00\x89\xab\xcd\xef",
292            [
293                Input(b"abcdef"),
294                WindowSizeChange(WindowSize::new(0x89ab, 0xcdef)),
295            ],
296        );
297    }
298
299    #[test]
300    fn decode_input_escape_escape_last() {
301        assert_decode_input(b"abcdef\xff\xff", [Input(b"abcdef"), Input(b"\xff")]);
302    }
303
304    #[test]
305    fn decode_input_escape_garbage_last() {
306        assert_decode_input(b"abcdef\xff\x01", [Input(b"abcdef"), Input(b"\xff\x01")]);
307    }
308
309    #[test]
310    fn decode_input_kitchen_sink() {
311        assert_decode_input(
312            b"\xff\xff\x00\xff\x00\x89\xab\xcd\xff\xff\xff\xffabc",
313            [
314                Input(b"\xff"),
315                Input(b"\x00"),
316                WindowSizeChange(WindowSize::new(0x89ab, 0xcdff)),
317                Input(b"\xff"),
318                Input(b"\xffa"),
319                Input(b"bc"),
320            ],
321        );
322    }
323}