hotfix_encoding/
raw_decoder.rs1use std::ops::Range;
2
3use crate::config::{Config, GetConfig};
4use crate::error::DecodeError;
5use crate::utils;
6
7#[derive(Debug)]
9pub struct RawFrame<T> {
10 pub data: T,
13 pub begin_string: Range<usize>,
15 pub payload: Range<usize>,
18}
19
20impl<T> RawFrame<T>
21where
22 T: AsRef<[u8]>,
23{
24 pub fn as_bytes(&self) -> &[u8] {
40 self.data.as_ref()
41 }
42
43 pub fn begin_string(&self) -> &[u8] {
60 &self.as_bytes()[self.begin_string.clone()]
61 }
62
63 pub fn payload(&self) -> &[u8] {
86 &self.as_bytes()[self.payload.clone()]
87 }
88}
89
90#[derive(Debug, Clone, Default)]
97pub struct RawDecoder<C = Config> {
98 config: C,
99}
100
101impl RawDecoder {
102 pub fn new() -> Self {
104 Self::default()
105 }
106
107 pub fn decode<T>(&self, src: T) -> Result<RawFrame<T>, DecodeError>
109 where
110 T: AsRef<[u8]>,
111 {
112 let data = src.as_ref();
113 let len = data.len();
114 if len < utils::MIN_FIX_MESSAGE_LEN_IN_BYTES {
115 return Err(DecodeError::Invalid);
116 }
117
118 let header_info =
119 HeaderInfo::parse(data, self.config().separator).ok_or(DecodeError::Invalid)?;
120
121 utils::verify_body_length(
122 data,
123 header_info.field_1.end + 1,
124 header_info.nominal_body_len,
125 )?;
126
127 if self.config.verify_checksum && self.config.separator == b'\x01' {
128 utils::verify_checksum(data)?;
129 }
130
131 Ok(RawFrame {
132 data: src,
133 begin_string: header_info.field_0,
134 payload: header_info.field_1.end + 1..len - utils::FIELD_CHECKSUM_LEN_IN_BYTES,
135 })
136 }
137}
138
139impl<C> GetConfig for RawDecoder<C> {
140 type Config = C;
141
142 fn config(&self) -> &C {
143 &self.config
144 }
145
146 fn config_mut(&mut self) -> &mut C {
147 &mut self.config
148 }
149}
150
151#[derive(Debug)]
152pub enum ParserState {
153 Empty,
154 Header(HeaderInfo, usize),
155 Failed,
156}
157
158#[derive(Debug, Clone)]
159pub struct HeaderInfo {
160 pub(crate) field_0: Range<usize>,
161 pub(crate) field_1: Range<usize>,
162 pub(crate) nominal_body_len: usize,
163}
164
165impl HeaderInfo {
166 pub fn parse(data: &[u8], separator: u8) -> Option<Self> {
167 let mut info = Self {
168 field_0: 0..1,
169 field_1: 0..1,
170 nominal_body_len: 0,
171 };
172
173 let mut iterator = data.iter();
174 let mut find_byte = |byte| iterator.position(|b| *b == byte);
175 let mut i = 0;
176
177 i += find_byte(b'=')? + 1;
178 info.field_0.start = i;
179 i += find_byte(separator)?;
180 info.field_0.end = i;
181 i += 1;
182
183 i += find_byte(b'=')? + 1;
184 info.field_1.start = i;
185 i += find_byte(separator)?;
186 info.field_1.end = i;
187
188 for byte in &data[info.field_1.clone()] {
189 info.nominal_body_len = info
190 .nominal_body_len
191 .wrapping_mul(10)
192 .wrapping_add(byte.wrapping_sub(b'0') as usize);
193 }
194
195 Some(info)
196 }
197}
198
199#[cfg(test)]
200mod test {
201 use super::*;
202
203 fn new_decoder() -> RawDecoder {
204 let config = Config {
205 separator: b'|',
206 ..Config::default()
207 };
208
209 let mut decoder = RawDecoder::new();
210 *decoder.config_mut() = config;
211 decoder
212 }
213
214 #[test]
215 fn empty_message_is_invalid() {
216 let decoder = new_decoder();
217 assert!(matches!(
218 decoder.decode(&[] as &[u8]),
219 Err(DecodeError::Invalid)
220 ));
221 }
222
223 #[test]
224 fn sample_message_is_valid() {
225 let decoder = new_decoder();
226 let msg = "8=FIX.4.2|9=40|35=D|49=AFUNDMGR|56=ABROKER|15=USD|59=0|10=091|".as_bytes();
227 let frame = decoder.decode(msg).unwrap();
228 assert_eq!(frame.begin_string(), b"FIX.4.2");
229 assert_eq!(frame.payload(), b"35=D|49=AFUNDMGR|56=ABROKER|15=USD|59=0|");
230 }
231
232 #[test]
233 fn message_with_only_msg_type_tag_is_valid() {
234 let decoder = new_decoder();
235 let msg = "8=?|9=5|35=?|10=183|".as_bytes();
236 let frame = decoder.decode(msg).unwrap();
237 assert_eq!(frame.begin_string(), b"?");
238 assert_eq!(frame.payload(), b"35=?|");
239 }
240
241 #[test]
242 fn message_with_empty_payload_is_invalid() {
243 let decoder = new_decoder();
244 let msg = "8=?|9=5|10=082|".as_bytes();
245 assert!(matches!(decoder.decode(msg), Err(DecodeError::Invalid)));
246 }
247
248 #[test]
249 fn message_with_bad_checksum_is_invalid() {
250 let mut decoder = new_decoder();
251 decoder.config_mut().separator = 0x01;
252 decoder.config_mut().verify_checksum = true;
253 let msg =
254 "8=FIX.4.2|9=40|35=D|49=AFUNDMGR|56=ABROKER|15=USD|59=0|10=000|".replace('|', "\u{01}");
255 assert!(matches!(decoder.decode(&msg), Err(DecodeError::CheckSum)));
256 }
257
258 #[test]
259 fn edge_cases_dont_cause_panic() {
260 let decoder = new_decoder();
261 decoder.decode("8=|9=0|10=225|".as_bytes()).ok();
262 decoder.decode("8=|9=0|10=|".as_bytes()).ok();
263 decoder.decode("8====|9=0|10=|".as_bytes()).ok();
264 decoder.decode("|||9=0|10=|".as_bytes()).ok();
265 decoder.decode("9999999999999".as_bytes()).ok();
266 decoder.decode("-9999999999999".as_bytes()).ok();
267 decoder.decode("==============".as_bytes()).ok();
268 decoder.decode("9999999999999|".as_bytes()).ok();
269 decoder.decode("|999999999999=|".as_bytes()).ok();
270 decoder.decode("|999=999999999999999999|=".as_bytes()).ok();
271 }
272}