bitfold_protocol/command_codec/
decoder.rs

1//! Command packet decoding and deserialization.
2//!
3//! Provides efficient binary decoding of protocol commands received from the network.
4//! This module handles the deserialization of command packets, including support for
5//! compression and checksum validation through companion modules.
6
7use std::io::{self, Cursor, Read};
8
9use bitfold_core::shared::SharedBytes;
10use byteorder::{BigEndian, ReadBytesExt};
11
12use super::super::command::{CommandPacket, ProtocolCommand};
13
14/// Deserializes commands from network bytes.
15pub struct CommandDecoder;
16
17impl CommandDecoder {
18    /// Decodes a single command from a cursor
19    pub fn decode_command(cursor: &mut Cursor<&[u8]>) -> io::Result<ProtocolCommand> {
20        let cmd_type = cursor.read_u8()?;
21
22        let command = match cmd_type {
23            1 => {
24                // SendReliable
25                let channel_id = cursor.read_u8()?;
26                let sequence = cursor.read_u16::<BigEndian>()?;
27                let ordered = cursor.read_u8()? != 0;
28                let data_len = cursor.read_u16::<BigEndian>()? as usize;
29                let mut data_vec = vec![0u8; data_len];
30                cursor.read_exact(&mut data_vec)?;
31                let data = SharedBytes::from_vec(data_vec);
32                ProtocolCommand::SendReliable { channel_id, sequence, ordered, data }
33            }
34            2 => {
35                // SendUnreliable
36                let channel_id = cursor.read_u8()?;
37                let data_len = cursor.read_u16::<BigEndian>()? as usize;
38                let mut data_vec = vec![0u8; data_len];
39                cursor.read_exact(&mut data_vec)?;
40                let data = SharedBytes::from_vec(data_vec);
41                ProtocolCommand::SendUnreliable { channel_id, data }
42            }
43            3 => {
44                // SendUnreliableSequenced
45                let channel_id = cursor.read_u8()?;
46                let sequence = cursor.read_u16::<BigEndian>()?;
47                let data_len = cursor.read_u16::<BigEndian>()? as usize;
48                let mut data_vec = vec![0u8; data_len];
49                cursor.read_exact(&mut data_vec)?;
50                let data = SharedBytes::from_vec(data_vec);
51                ProtocolCommand::SendUnreliableSequenced { channel_id, sequence, data }
52            }
53            4 => {
54                // SendUnsequenced
55                let channel_id = cursor.read_u8()?;
56                let unsequenced_group = cursor.read_u16::<BigEndian>()?;
57                let data_len = cursor.read_u16::<BigEndian>()? as usize;
58                let mut data_vec = vec![0u8; data_len];
59                cursor.read_exact(&mut data_vec)?;
60                let data = SharedBytes::from_vec(data_vec);
61                ProtocolCommand::SendUnsequenced { channel_id, unsequenced_group, data }
62            }
63            5 => {
64                // SendFragment (reliable)
65                let channel_id = cursor.read_u8()?;
66                let sequence = cursor.read_u16::<BigEndian>()?;
67                let ordered = cursor.read_u8()? != 0;
68                let fragment_id = cursor.read_u8()?;
69                let fragment_count = cursor.read_u8()?;
70                let data_len = cursor.read_u16::<BigEndian>()? as usize;
71                let mut data_vec = vec![0u8; data_len];
72                cursor.read_exact(&mut data_vec)?;
73                let data = SharedBytes::from_vec(data_vec);
74                ProtocolCommand::SendFragment {
75                    channel_id,
76                    sequence,
77                    ordered,
78                    fragment_id,
79                    fragment_count,
80                    data,
81                }
82            }
83            6 => {
84                // SendUnreliableFragment
85                let channel_id = cursor.read_u8()?;
86                let sequence = cursor.read_u16::<BigEndian>()?;
87                let fragment_id = cursor.read_u8()?;
88                let fragment_count = cursor.read_u8()?;
89                let data_len = cursor.read_u16::<BigEndian>()? as usize;
90                let mut data_vec = vec![0u8; data_len];
91                cursor.read_exact(&mut data_vec)?;
92                let data = SharedBytes::from_vec(data_vec);
93                ProtocolCommand::SendUnreliableFragment {
94                    channel_id,
95                    sequence,
96                    fragment_id,
97                    fragment_count,
98                    data,
99                }
100            }
101            7 => {
102                // Acknowledge
103                let sequence = cursor.read_u16::<BigEndian>()?;
104                let received_mask = cursor.read_u32::<BigEndian>()?;
105                // sent_time is optional, check if there's more data
106                let sent_time = if cursor.position() < cursor.get_ref().len() as u64 {
107                    Some(cursor.read_u32::<BigEndian>()?)
108                } else {
109                    None
110                };
111                ProtocolCommand::Acknowledge { sequence, received_mask, sent_time }
112            }
113            8 => {
114                // Ping
115                let timestamp = cursor.read_u32::<BigEndian>()?;
116                ProtocolCommand::Ping { timestamp }
117            }
118            9 => {
119                // Pong
120                let timestamp = cursor.read_u32::<BigEndian>()?;
121                ProtocolCommand::Pong { timestamp }
122            }
123            10 => {
124                // Connect
125                let channels = cursor.read_u8()?;
126                let mtu = cursor.read_u16::<BigEndian>()?;
127                let protocol_version = cursor.read_u16::<BigEndian>()?;
128                let outgoing_session_id = cursor.read_u16::<BigEndian>()?;
129                let connect_id = cursor.read_u32::<BigEndian>()?;
130                ProtocolCommand::Connect {
131                    channels,
132                    mtu,
133                    protocol_version,
134                    outgoing_session_id,
135                    connect_id,
136                }
137            }
138            11 => {
139                // VerifyConnect
140                let peer_id = cursor.read_u16::<BigEndian>()?;
141                let channels = cursor.read_u8()?;
142                let mtu = cursor.read_u16::<BigEndian>()?;
143                let incoming_session_id = cursor.read_u16::<BigEndian>()?;
144                let outgoing_session_id = cursor.read_u16::<BigEndian>()?;
145                let window_size = cursor.read_u32::<BigEndian>()?;
146                ProtocolCommand::VerifyConnect {
147                    peer_id,
148                    channels,
149                    mtu,
150                    incoming_session_id,
151                    outgoing_session_id,
152                    window_size,
153                }
154            }
155            12 => {
156                // Disconnect
157                let reason = cursor.read_u32::<BigEndian>()?;
158                ProtocolCommand::Disconnect { reason }
159            }
160            13 => {
161                // BandwidthLimit
162                let incoming = cursor.read_u32::<BigEndian>()?;
163                let outgoing = cursor.read_u32::<BigEndian>()?;
164                ProtocolCommand::BandwidthLimit { incoming, outgoing }
165            }
166            14 => {
167                // ThrottleConfigure
168                let interval = cursor.read_u32::<BigEndian>()?;
169                let acceleration = cursor.read_u32::<BigEndian>()?;
170                let deceleration = cursor.read_u32::<BigEndian>()?;
171                ProtocolCommand::ThrottleConfigure { interval, acceleration, deceleration }
172            }
173            15 => {
174                // PMTUProbe
175                let size = cursor.read_u16::<BigEndian>()?;
176                let token = cursor.read_u32::<BigEndian>()?;
177                let payload_len = cursor.read_u16::<BigEndian>()? as usize;
178                let mut payload = vec![0u8; payload_len];
179                cursor.read_exact(&mut payload)?;
180                ProtocolCommand::PMTUProbe { size, token, payload: SharedBytes::from_vec(payload) }
181            }
182            16 => {
183                // PMTUReply
184                let size = cursor.read_u16::<BigEndian>()?;
185                let token = cursor.read_u32::<BigEndian>()?;
186                ProtocolCommand::PMTUReply { size, token }
187            }
188            _ => {
189                return Err(io::Error::new(
190                    io::ErrorKind::InvalidData,
191                    format!("Unknown command type: {}", cmd_type),
192                ));
193            }
194        };
195
196        Ok(command)
197    }
198
199    /// Decodes a command packet containing multiple commands
200    pub fn decode_packet(data: &[u8]) -> io::Result<CommandPacket> {
201        let mut cursor = Cursor::new(data);
202        let mut packet = CommandPacket::new();
203
204        // Read command count
205        let cmd_count = cursor.read_u8()?;
206
207        // Read each command
208        for _ in 0..cmd_count {
209            let cmd_len = cursor.read_u16::<BigEndian>()? as usize;
210            let pos = cursor.position() as usize;
211
212            if pos + cmd_len > data.len() {
213                return Err(io::Error::new(
214                    io::ErrorKind::UnexpectedEof,
215                    "Command length exceeds buffer",
216                ));
217            }
218
219            let cmd_data = &data[pos..pos + cmd_len];
220            let mut cmd_cursor = Cursor::new(cmd_data);
221            let command = Self::decode_command(&mut cmd_cursor)?;
222
223            packet.add_command(command);
224            cursor.set_position((pos + cmd_len) as u64);
225        }
226
227        Ok(packet)
228    }
229
230    /// Validates and strips the CRC32 checksum from packet data.
231    /// Returns the data without checksum if valid, or an error if checksum fails.
232    ///
233    /// This is a convenience wrapper around the checksum module's validation function.
234    pub fn validate_and_strip_checksum(data: &[u8]) -> io::Result<&[u8]> {
235        super::checksum::validate_and_strip_checksum(data)
236    }
237
238    /// Decompresses data based on the 1-byte header.
239    /// Header format: `[algorithm_id][data]`
240    /// - 0: Uncompressed
241    /// - 1: Zlib
242    /// - 2: LZ4
243    ///
244    /// This is a convenience wrapper around the compression module's decompression function.
245    pub fn decompress(data: &[u8]) -> io::Result<Vec<u8>> {
246        super::compression::decompress(data)
247    }
248}