use std::ops::Range;
use crate::config::{Config, GetConfig};
use crate::error::DecodeError;
use crate::utils;
#[derive(Debug)]
pub struct RawFrame<T> {
pub data: T,
pub begin_string: Range<usize>,
pub payload: Range<usize>,
}
impl<T> RawFrame<T>
where
T: AsRef<[u8]>,
{
pub fn as_bytes(&self) -> &[u8] {
self.data.as_ref()
}
pub fn begin_string(&self) -> &[u8] {
&self.as_bytes()[self.begin_string.clone()]
}
pub fn payload(&self) -> &[u8] {
&self.as_bytes()[self.payload.clone()]
}
}
#[derive(Debug, Clone, Default)]
pub struct RawDecoder<C = Config> {
config: C,
}
impl RawDecoder {
pub fn new() -> Self {
Self::default()
}
pub fn decode<T>(&self, src: T) -> Result<RawFrame<T>, DecodeError>
where
T: AsRef<[u8]>,
{
let data = src.as_ref();
let len = data.len();
if len < utils::MIN_FIX_MESSAGE_LEN_IN_BYTES {
return Err(DecodeError::Invalid);
}
let header_info =
HeaderInfo::parse(data, self.config().separator).ok_or(DecodeError::Invalid)?;
utils::verify_body_length(
data,
header_info.field_1.end + 1,
header_info.nominal_body_len,
)?;
if self.config.verify_checksum && self.config.separator == b'\x01' {
utils::verify_checksum(data)?;
}
Ok(RawFrame {
data: src,
begin_string: header_info.field_0,
payload: header_info.field_1.end + 1..len - utils::FIELD_CHECKSUM_LEN_IN_BYTES,
})
}
}
impl<C> GetConfig for RawDecoder<C> {
type Config = C;
fn config(&self) -> &C {
&self.config
}
fn config_mut(&mut self) -> &mut C {
&mut self.config
}
}
#[derive(Debug)]
pub enum ParserState {
Empty,
Header(HeaderInfo, usize),
Failed,
}
#[derive(Debug, Clone)]
pub struct HeaderInfo {
pub(crate) field_0: Range<usize>,
pub(crate) field_1: Range<usize>,
pub(crate) nominal_body_len: usize,
}
impl HeaderInfo {
pub fn parse(data: &[u8], separator: u8) -> Option<Self> {
let mut info = Self {
field_0: 0..1,
field_1: 0..1,
nominal_body_len: 0,
};
let mut iterator = data.iter();
let mut find_byte = |byte| iterator.position(|b| *b == byte);
let mut i = 0;
i += find_byte(b'=')? + 1;
info.field_0.start = i;
i += find_byte(separator)?;
info.field_0.end = i;
i += 1;
i += find_byte(b'=')? + 1;
info.field_1.start = i;
i += find_byte(separator)?;
info.field_1.end = i;
for byte in &data[info.field_1.clone()] {
info.nominal_body_len = info
.nominal_body_len
.wrapping_mul(10)
.wrapping_add(byte.wrapping_sub(b'0') as usize);
}
Some(info)
}
}
#[cfg(test)]
mod test {
use super::*;
fn new_decoder() -> RawDecoder {
let config = Config {
separator: b'|',
..Config::default()
};
let mut decoder = RawDecoder::new();
*decoder.config_mut() = config;
decoder
}
#[test]
fn empty_message_is_invalid() {
let decoder = new_decoder();
assert!(matches!(
decoder.decode(&[] as &[u8]),
Err(DecodeError::Invalid)
));
}
#[test]
fn sample_message_is_valid() {
let decoder = new_decoder();
let msg = "8=FIX.4.2|9=40|35=D|49=AFUNDMGR|56=ABROKER|15=USD|59=0|10=091|".as_bytes();
let frame = decoder.decode(msg).unwrap();
assert_eq!(frame.begin_string(), b"FIX.4.2");
assert_eq!(frame.payload(), b"35=D|49=AFUNDMGR|56=ABROKER|15=USD|59=0|");
}
#[test]
fn message_with_only_msg_type_tag_is_valid() {
let decoder = new_decoder();
let msg = "8=?|9=5|35=?|10=183|".as_bytes();
let frame = decoder.decode(msg).unwrap();
assert_eq!(frame.begin_string(), b"?");
assert_eq!(frame.payload(), b"35=?|");
}
#[test]
fn message_with_empty_payload_is_invalid() {
let decoder = new_decoder();
let msg = "8=?|9=5|10=082|".as_bytes();
assert!(matches!(decoder.decode(msg), Err(DecodeError::Invalid)));
}
#[test]
fn message_with_bad_checksum_is_invalid() {
let mut decoder = new_decoder();
decoder.config_mut().separator = 0x01;
decoder.config_mut().verify_checksum = true;
let msg =
"8=FIX.4.2|9=40|35=D|49=AFUNDMGR|56=ABROKER|15=USD|59=0|10=000|".replace('|', "\u{01}");
assert!(matches!(decoder.decode(&msg), Err(DecodeError::CheckSum)));
}
#[test]
fn edge_cases_dont_cause_panic() {
let decoder = new_decoder();
decoder.decode("8=|9=0|10=225|".as_bytes()).ok();
decoder.decode("8=|9=0|10=|".as_bytes()).ok();
decoder.decode("8====|9=0|10=|".as_bytes()).ok();
decoder.decode("|||9=0|10=|".as_bytes()).ok();
decoder.decode("9999999999999".as_bytes()).ok();
decoder.decode("-9999999999999".as_bytes()).ok();
decoder.decode("==============".as_bytes()).ok();
decoder.decode("9999999999999|".as_bytes()).ok();
decoder.decode("|999999999999=|".as_bytes()).ok();
decoder.decode("|999=999999999999999999|=".as_bytes()).ok();
}
}