breakwater_parser/
refactored.rs

1use std::sync::Arc;
2
3use crate::{
4    FrameBuffer, HELP_TEXT, Parser,
5    original::{
6        HELP_PATTERN, OFFSET_PATTERN, PB_PATTERN, PX_PATTERN, SIZE_PATTERN,
7        parse_pixel_coordinates, simd_unhex,
8    },
9};
10
11const PARSER_LOOKAHEAD: usize = "PX 1234 1234 rrggbbaa\n".len(); // Longest possible command
12
13pub struct RefactoredParser<FB: FrameBuffer> {
14    connection_x_offset: usize,
15    connection_y_offset: usize,
16    fb: Arc<FB>,
17}
18
19impl<FB: FrameBuffer> RefactoredParser<FB> {
20    pub fn new(fb: Arc<FB>) -> Self {
21        Self {
22            connection_x_offset: 0,
23            connection_y_offset: 0,
24            fb,
25        }
26    }
27
28    #[inline(always)]
29    fn handle_pixel(
30        &self,
31        buffer: &[u8],
32        mut idx: usize,
33        response: &mut Vec<u8>,
34    ) -> (usize, usize) {
35        let previous = idx;
36        idx += 3;
37
38        let (mut x, mut y, present) = parse_pixel_coordinates(buffer.as_ptr(), &mut idx);
39
40        if present {
41            x += self.connection_x_offset;
42            y += self.connection_y_offset;
43
44            // Separator between coordinates and color
45            if unsafe { *buffer.get_unchecked(idx) } == b' ' {
46                idx += 1;
47
48                // TODO: Determine what clients use more: RGB, RGBA or gg variant.
49                // If RGBA is used more often move the RGB code below the RGBA code
50
51                // Must be followed by 6 bytes RGB and newline or ...
52                if unsafe { *buffer.get_unchecked(idx + 6) } == b'\n' {
53                    idx += 7;
54                    self.handle_rgb(idx, buffer, x, y);
55                    (idx, idx)
56                }
57                // ... or must be followed by 8 bytes RGBA and newline
58                else if unsafe { *buffer.get_unchecked(idx + 8) } == b'\n' {
59                    idx += 9;
60                    self.handle_rgba(idx, buffer, x, y);
61                    (idx, idx)
62                }
63                // ... for the efficient/lazy clients
64                else if unsafe { *buffer.get_unchecked(idx + 2) } == b'\n' {
65                    idx += 3;
66                    self.handle_gray(idx, buffer, x, y);
67                    (idx, idx)
68                } else {
69                    (idx, previous)
70                }
71            }
72            // End of command to read Pixel value
73            else if unsafe { *buffer.get_unchecked(idx) } == b'\n' {
74                idx += 1;
75                self.handle_get_pixel(response, x, y);
76                (idx, idx)
77            } else {
78                (idx, previous)
79            }
80        } else {
81            (idx, previous)
82        }
83    }
84
85    #[inline(always)]
86    fn handle_binary_pixel(&self, buffer: &[u8], mut idx: usize) -> (usize, usize) {
87        let previous = idx;
88        idx += 2;
89
90        let command_bytes = unsafe { (buffer.as_ptr().add(idx) as *const u64).read_unaligned() };
91
92        let x = u16::from_le((command_bytes) as u16);
93        let y = u16::from_le((command_bytes >> 16) as u16);
94        let rgba = u32::from_le((command_bytes >> 32) as u32);
95
96        // TODO: Support alpha channel (behind alpha feature flag)
97        self.fb.set(x as usize, y as usize, rgba & 0x00ff_ffff);
98
99        idx += 8;
100        (idx, previous)
101    }
102
103    #[inline(always)]
104    fn handle_offset(&mut self, idx: &mut usize, buffer: &[u8]) {
105        let (x, y, present) = parse_pixel_coordinates(buffer.as_ptr(), idx);
106
107        // End of command to set offset
108        if present && unsafe { *buffer.get_unchecked(*idx) } == b'\n' {
109            self.connection_x_offset = x;
110            self.connection_y_offset = y;
111        }
112    }
113
114    #[inline(always)]
115    fn handle_size(&self, response: &mut Vec<u8>) {
116        response.extend_from_slice(
117            format!("SIZE {} {}\n", self.fb.get_width(), self.fb.get_height()).as_bytes(),
118        );
119    }
120
121    #[inline(always)]
122    fn handle_help(&self, response: &mut Vec<u8>) {
123        response.extend_from_slice(HELP_TEXT);
124    }
125
126    #[inline(always)]
127    fn handle_rgb(&self, idx: usize, buffer: &[u8], x: usize, y: usize) {
128        let rgba: u32 = simd_unhex(unsafe { buffer.as_ptr().add(idx - 7) });
129
130        self.fb.set(x, y, rgba & 0x00ff_ffff);
131    }
132
133    #[cfg(not(feature = "alpha"))]
134    #[inline(always)]
135    fn handle_rgba(&self, idx: usize, buffer: &[u8], x: usize, y: usize) {
136        let rgba: u32 = simd_unhex(unsafe { buffer.as_ptr().add(idx - 9) });
137
138        self.fb.set(x, y, rgba & 0x00ff_ffff);
139    }
140
141    #[cfg(feature = "alpha")]
142    #[inline(always)]
143    fn handle_rgba(&self, idx: usize, buffer: &[u8], x: usize, y: usize) {
144        let rgba: u32 = simd_unhex(unsafe { buffer.as_ptr().add(idx - 9) });
145
146        let alpha = (rgba >> 24) & 0xff;
147
148        if alpha == 0 || x >= self.fb.get_width() || y >= self.fb.get_height() {
149            return;
150        }
151
152        let alpha_comp = 0xff - alpha;
153        let current = unsafe { self.fb.get_unchecked(x, y) };
154        let r = (rgba >> 16) & 0xff;
155        let g = (rgba >> 8) & 0xff;
156        let b = rgba & 0xff;
157
158        let r: u32 = (((current >> 24) & 0xff) * alpha_comp + r * alpha) / 0xff;
159        let g: u32 = (((current >> 16) & 0xff) * alpha_comp + g * alpha) / 0xff;
160        let b: u32 = (((current >> 8) & 0xff) * alpha_comp + b * alpha) / 0xff;
161
162        self.fb.set(x, y, (r << 16) | (g << 8) | b);
163    }
164
165    #[inline(always)]
166    fn handle_gray(&self, idx: usize, buffer: &[u8], x: usize, y: usize) {
167        // FIXME: Read that two bytes directly instead of going through the whole SIMD vector setup.
168        // Or - as an alternative - still do the SIMD part but only load two bytes.
169        let base: u32 = simd_unhex(unsafe { buffer.as_ptr().add(idx - 3) }) & 0xff;
170
171        let rgba: u32 = (base << 16) | (base << 8) | base;
172
173        self.fb.set(x, y, rgba);
174    }
175
176    #[inline(always)]
177    fn handle_get_pixel(&self, response: &mut Vec<u8>, x: usize, y: usize) {
178        if let Some(rgb) = self.fb.get(x, y) {
179            response.extend_from_slice(
180                format!(
181                    "PX {} {} {:06x}\n",
182                    // We don't want to return the actual (absolute) coordinates, the client should also get the result offseted
183                    x - self.connection_x_offset,
184                    y - self.connection_y_offset,
185                    rgb.to_be() >> 8
186                )
187                .as_bytes(),
188            );
189        }
190    }
191}
192
193impl<FB: FrameBuffer> Parser for RefactoredParser<FB> {
194    fn parse(&mut self, buffer: &[u8], response: &mut Vec<u8>) -> usize {
195        let mut last_byte_parsed = 0;
196
197        let mut i = 0; // We can't use a for loop here because Rust don't lets use skip characters by incrementing i
198        let loop_end = buffer.len().saturating_sub(PARSER_LOOKAHEAD); // Let's extract the .len() call and the subtraction into it's own variable so we only compute it once
199
200        while i < loop_end {
201            let current_command =
202                unsafe { (buffer.as_ptr().add(i) as *const u64).read_unaligned() };
203            if current_command & 0x00ff_ffff == PX_PATTERN {
204                (i, last_byte_parsed) = self.handle_pixel(buffer, i, response);
205            } else if cfg!(feature = "binary-set-pixel")
206                && current_command & 0x0000_ffff == PB_PATTERN
207            {
208                (i, last_byte_parsed) = self.handle_binary_pixel(buffer, i);
209            } else if current_command & 0x00ff_ffff_ffff_ffff == OFFSET_PATTERN {
210                i += 7;
211                self.handle_offset(&mut i, buffer);
212                last_byte_parsed = i;
213            } else if current_command & 0xffff_ffff == SIZE_PATTERN {
214                i += 4;
215                last_byte_parsed = i;
216                self.handle_size(response);
217            } else if current_command & 0xffff_ffff == HELP_PATTERN {
218                i += 4;
219                last_byte_parsed = i;
220                self.handle_help(response);
221            } else {
222                i += 1;
223            }
224        }
225
226        last_byte_parsed.wrapping_sub(1)
227    }
228
229    fn parser_lookahead(&self) -> usize {
230        PARSER_LOOKAHEAD
231    }
232}