bitfold_protocol/command_codec/
encoder.rs

1//! Command packet encoding.
2//!
3//! Provides efficient binary serialization of protocol commands
4//! for transmission over the network.
5
6use std::io::{self, Write};
7
8use byteorder::{BigEndian, WriteBytesExt};
9
10use super::super::command::{CommandPacket, ProtocolCommand};
11
12/// Serializes a command packet into bytes for transmission.
13pub struct CommandEncoder;
14
15impl CommandEncoder {
16    /// Encodes a single command into the provided buffer (appends bytes).
17    pub fn encode_command_into(buffer: &mut Vec<u8>, command: &ProtocolCommand) -> io::Result<()> {
18        // Write command type
19        buffer.write_u8(command.command_type())?;
20
21        match command {
22            ProtocolCommand::SendReliable { channel_id, sequence, ordered, data } => {
23                buffer.write_u8(*channel_id)?;
24                buffer.write_u16::<BigEndian>(*sequence)?;
25                buffer.write_u8(if *ordered { 1 } else { 0 })?;
26                buffer.write_u16::<BigEndian>(data.len() as u16)?;
27                buffer.write_all(data.as_slice())?;
28            }
29            ProtocolCommand::SendUnreliable { channel_id, data } => {
30                buffer.write_u8(*channel_id)?;
31                buffer.write_u16::<BigEndian>(data.len() as u16)?;
32                buffer.write_all(data.as_slice())?;
33            }
34            ProtocolCommand::SendUnreliableSequenced { channel_id, sequence, data } => {
35                buffer.write_u8(*channel_id)?;
36                buffer.write_u16::<BigEndian>(*sequence)?;
37                buffer.write_u16::<BigEndian>(data.len() as u16)?;
38                buffer.write_all(data.as_slice())?;
39            }
40            ProtocolCommand::SendUnsequenced { channel_id, unsequenced_group, data } => {
41                buffer.write_u8(*channel_id)?;
42                buffer.write_u16::<BigEndian>(*unsequenced_group)?;
43                buffer.write_u16::<BigEndian>(data.len() as u16)?;
44                buffer.write_all(data.as_slice())?;
45            }
46            ProtocolCommand::SendFragment {
47                channel_id,
48                sequence,
49                ordered,
50                fragment_id,
51                fragment_count,
52                data,
53            } => {
54                buffer.write_u8(*channel_id)?;
55                buffer.write_u16::<BigEndian>(*sequence)?;
56                buffer.write_u8(if *ordered { 1 } else { 0 })?;
57                buffer.write_u8(*fragment_id)?;
58                buffer.write_u8(*fragment_count)?;
59                buffer.write_u16::<BigEndian>(data.len() as u16)?;
60                buffer.write_all(data.as_slice())?;
61            }
62            ProtocolCommand::SendUnreliableFragment {
63                channel_id,
64                sequence,
65                fragment_id,
66                fragment_count,
67                data,
68            } => {
69                buffer.write_u8(*channel_id)?;
70                buffer.write_u16::<BigEndian>(*sequence)?;
71                buffer.write_u8(*fragment_id)?;
72                buffer.write_u8(*fragment_count)?;
73                buffer.write_u16::<BigEndian>(data.len() as u16)?;
74                buffer.write_all(data.as_slice())?;
75            }
76            ProtocolCommand::Acknowledge { sequence, received_mask, sent_time } => {
77                buffer.write_u16::<BigEndian>(*sequence)?;
78                buffer.write_u32::<BigEndian>(*received_mask)?;
79                if let Some(time) = sent_time {
80                    buffer.write_u32::<BigEndian>(*time)?;
81                }
82            }
83            ProtocolCommand::Ping { timestamp } => {
84                buffer.write_u32::<BigEndian>(*timestamp)?;
85            }
86            ProtocolCommand::Pong { timestamp } => {
87                buffer.write_u32::<BigEndian>(*timestamp)?;
88            }
89            ProtocolCommand::Connect {
90                channels,
91                mtu,
92                protocol_version,
93                outgoing_session_id,
94                connect_id,
95            } => {
96                buffer.write_u8(*channels)?;
97                buffer.write_u16::<BigEndian>(*mtu)?;
98                buffer.write_u16::<BigEndian>(*protocol_version)?;
99                buffer.write_u16::<BigEndian>(*outgoing_session_id)?;
100                buffer.write_u32::<BigEndian>(*connect_id)?;
101            }
102            ProtocolCommand::VerifyConnect {
103                peer_id,
104                channels,
105                mtu,
106                incoming_session_id,
107                outgoing_session_id,
108                window_size,
109            } => {
110                buffer.write_u16::<BigEndian>(*peer_id)?;
111                buffer.write_u8(*channels)?;
112                buffer.write_u16::<BigEndian>(*mtu)?;
113                buffer.write_u16::<BigEndian>(*incoming_session_id)?;
114                buffer.write_u16::<BigEndian>(*outgoing_session_id)?;
115                buffer.write_u32::<BigEndian>(*window_size)?;
116            }
117            ProtocolCommand::Disconnect { reason } => {
118                buffer.write_u32::<BigEndian>(*reason)?;
119            }
120            ProtocolCommand::BandwidthLimit { incoming, outgoing } => {
121                buffer.write_u32::<BigEndian>(*incoming)?;
122                buffer.write_u32::<BigEndian>(*outgoing)?;
123            }
124            ProtocolCommand::ThrottleConfigure { interval, acceleration, deceleration } => {
125                buffer.write_u32::<BigEndian>(*interval)?;
126                buffer.write_u32::<BigEndian>(*acceleration)?;
127                buffer.write_u32::<BigEndian>(*deceleration)?;
128            }
129            ProtocolCommand::PMTUProbe { size, token, payload } => {
130                buffer.write_u16::<BigEndian>(*size)?;
131                buffer.write_u32::<BigEndian>(*token)?;
132                buffer.write_u16::<BigEndian>(payload.len() as u16)?;
133                buffer.write_all(payload.as_slice())?;
134            }
135            ProtocolCommand::PMTUReply { size, token } => {
136                buffer.write_u16::<BigEndian>(*size)?;
137                buffer.write_u32::<BigEndian>(*token)?;
138            }
139        }
140
141        Ok(())
142    }
143
144    /// Encodes a command packet into the provided buffer (appends bytes) without intermediate allocations per command.
145    pub fn encode_packet_into(buffer: &mut Vec<u8>, packet: &CommandPacket) -> io::Result<()> {
146        // Write command count
147        buffer.write_u8(packet.commands.len() as u8)?;
148
149        // Write each command with a length prefix. We first reserve space for the length,
150        // encode the command, then patch the length in place.
151        for command in &packet.commands {
152            let len_pos = buffer.len();
153            buffer.write_u16::<BigEndian>(0)?; // placeholder for length
154            let start = buffer.len();
155            Self::encode_command_into(buffer, command)?;
156            let cmd_len = buffer.len() - start;
157            // Patch the length
158            buffer[len_pos] = ((cmd_len >> 8) & 0xFF) as u8;
159            buffer[len_pos + 1] = (cmd_len & 0xFF) as u8;
160        }
161
162        Ok(())
163    }
164
165    /// Encodes a single command into a byte vector
166    pub fn encode_command(command: &ProtocolCommand) -> io::Result<Vec<u8>> {
167        let mut buffer = Vec::new();
168
169        // Write command type
170        buffer.write_u8(command.command_type())?;
171
172        match command {
173            ProtocolCommand::SendReliable { channel_id, sequence, ordered, data } => {
174                buffer.write_u8(*channel_id)?;
175                buffer.write_u16::<BigEndian>(*sequence)?;
176                buffer.write_u8(if *ordered { 1 } else { 0 })?;
177                buffer.write_u16::<BigEndian>(data.len() as u16)?;
178                buffer.write_all(data.as_slice())?;
179            }
180            ProtocolCommand::SendUnreliable { channel_id, data } => {
181                buffer.write_u8(*channel_id)?;
182                buffer.write_u16::<BigEndian>(data.len() as u16)?;
183                buffer.write_all(data.as_slice())?;
184            }
185            ProtocolCommand::SendUnreliableSequenced { channel_id, sequence, data } => {
186                buffer.write_u8(*channel_id)?;
187                buffer.write_u16::<BigEndian>(*sequence)?;
188                buffer.write_u16::<BigEndian>(data.len() as u16)?;
189                buffer.write_all(data.as_slice())?;
190            }
191            ProtocolCommand::SendUnsequenced { channel_id, unsequenced_group, data } => {
192                buffer.write_u8(*channel_id)?;
193                buffer.write_u16::<BigEndian>(*unsequenced_group)?;
194                buffer.write_u16::<BigEndian>(data.len() as u16)?;
195                buffer.write_all(data.as_slice())?;
196            }
197            ProtocolCommand::SendFragment {
198                channel_id,
199                sequence,
200                ordered,
201                fragment_id,
202                fragment_count,
203                data,
204            } => {
205                buffer.write_u8(*channel_id)?;
206                buffer.write_u16::<BigEndian>(*sequence)?;
207                buffer.write_u8(if *ordered { 1 } else { 0 })?;
208                buffer.write_u8(*fragment_id)?;
209                buffer.write_u8(*fragment_count)?;
210                buffer.write_u16::<BigEndian>(data.len() as u16)?;
211                buffer.write_all(data.as_slice())?;
212            }
213            ProtocolCommand::SendUnreliableFragment {
214                channel_id,
215                sequence,
216                fragment_id,
217                fragment_count,
218                data,
219            } => {
220                buffer.write_u8(*channel_id)?;
221                buffer.write_u16::<BigEndian>(*sequence)?;
222                buffer.write_u8(*fragment_id)?;
223                buffer.write_u8(*fragment_count)?;
224                buffer.write_u16::<BigEndian>(data.len() as u16)?;
225                buffer.write_all(data.as_slice())?;
226            }
227            ProtocolCommand::Acknowledge { sequence, received_mask, sent_time } => {
228                buffer.write_u16::<BigEndian>(*sequence)?;
229                buffer.write_u32::<BigEndian>(*received_mask)?;
230                if let Some(time) = sent_time {
231                    buffer.write_u32::<BigEndian>(*time)?;
232                }
233            }
234            ProtocolCommand::Ping { timestamp } => {
235                buffer.write_u32::<BigEndian>(*timestamp)?;
236            }
237            ProtocolCommand::Pong { timestamp } => {
238                buffer.write_u32::<BigEndian>(*timestamp)?;
239            }
240            ProtocolCommand::Connect {
241                channels,
242                mtu,
243                protocol_version,
244                outgoing_session_id,
245                connect_id,
246            } => {
247                buffer.write_u8(*channels)?;
248                buffer.write_u16::<BigEndian>(*mtu)?;
249                buffer.write_u16::<BigEndian>(*protocol_version)?;
250                buffer.write_u16::<BigEndian>(*outgoing_session_id)?;
251                buffer.write_u32::<BigEndian>(*connect_id)?;
252            }
253            ProtocolCommand::VerifyConnect {
254                peer_id,
255                channels,
256                mtu,
257                incoming_session_id,
258                outgoing_session_id,
259                window_size,
260            } => {
261                buffer.write_u16::<BigEndian>(*peer_id)?;
262                buffer.write_u8(*channels)?;
263                buffer.write_u16::<BigEndian>(*mtu)?;
264                buffer.write_u16::<BigEndian>(*incoming_session_id)?;
265                buffer.write_u16::<BigEndian>(*outgoing_session_id)?;
266                buffer.write_u32::<BigEndian>(*window_size)?;
267            }
268            ProtocolCommand::Disconnect { reason } => {
269                buffer.write_u32::<BigEndian>(*reason)?;
270            }
271            ProtocolCommand::BandwidthLimit { incoming, outgoing } => {
272                buffer.write_u32::<BigEndian>(*incoming)?;
273                buffer.write_u32::<BigEndian>(*outgoing)?;
274            }
275            ProtocolCommand::ThrottleConfigure { interval, acceleration, deceleration } => {
276                buffer.write_u32::<BigEndian>(*interval)?;
277                buffer.write_u32::<BigEndian>(*acceleration)?;
278                buffer.write_u32::<BigEndian>(*deceleration)?;
279            }
280            ProtocolCommand::PMTUProbe { size, token, payload } => {
281                buffer.write_u16::<BigEndian>(*size)?;
282                buffer.write_u32::<BigEndian>(*token)?;
283                buffer.write_u16::<BigEndian>(payload.len() as u16)?;
284                buffer.write_all(payload.as_slice())?;
285            }
286            ProtocolCommand::PMTUReply { size, token } => {
287                buffer.write_u16::<BigEndian>(*size)?;
288                buffer.write_u32::<BigEndian>(*token)?;
289            }
290        }
291
292        Ok(buffer)
293    }
294
295    /// Encodes a command packet with multiple commands
296    pub fn encode_packet(packet: &CommandPacket) -> io::Result<Vec<u8>> {
297        let mut buffer = Vec::new();
298
299        // Write command count
300        buffer.write_u8(packet.commands.len() as u8)?;
301
302        // Write each command with length prefix
303        for command in &packet.commands {
304            let cmd_bytes = Self::encode_command(command)?;
305            buffer.write_u16::<BigEndian>(cmd_bytes.len() as u16)?;
306            buffer.write_all(&cmd_bytes)?;
307        }
308
309        Ok(buffer)
310    }
311}