diff --git a/bench/ex_fix_bench.exs b/bench/ex_fix_bench.exs index 0a31b4f..3cbdcf5 100644 --- a/bench/ex_fix_bench.exs +++ b/bench/ex_fix_bench.exs @@ -42,6 +42,21 @@ defmodule ExFixBench do Parser.parse(data, @dictionary, nil, false) end + bench "Parse - Stage 1 (with SendingTime validation)", [data: get_parse_data_with_sending_time()] do + Parser.parse1(data, @dictionary, 12_345, true, true) # Assuming validate_sending_time is the 5th arg + end + + bench "Parse - Full Msg (with SendingTime validation)", [data: get_parse_data_with_sending_time()] do + Parser.parse(data, @dictionary, 12_345, true, true) # Assuming validate_sending_time is the 5th arg + end + + bench "Parse - Stage 1 (without SendingTime validation)", [data: get_parse_data_with_sending_time()] do + Parser.parse1(data, @dictionary, 12_345, true, false) # Assuming validate_sending_time is the 5th arg + end + + bench "Parse - Full Msg (without SendingTime validation)", [data: get_parse_data_with_sending_time()] do + Parser.parse(data, @dictionary, 12_345, true, false) # Assuming validate_sending_time is the 5th arg + end ## ## Private functions ## @@ -58,6 +73,22 @@ defmodule ExFixBench do :binary.replace(str_data, "|", << 1 >>, [:global]) end + defp get_parse_data_with_sending_time() do + # Similar to get_parse_data but ensures SendingTime (tag 52) is present + # For the benchmark, we'll use a valid SendingTime relative to now. + # Benchfella executes functions in `setup` block before each run, + # so we can't directly use DateTime.utc_now() here as it would be fixed at compile time. + # Instead, we'll construct a string that's highly likely to be valid. + # A more robust way would be to pass the current time into this function if Benchfella allowed it, + # or modify the parser to accept a "current time" for validation purposes in tests. + # For now, this simplification should be acceptable for benchmarking the validation logic itself. + sending_time = DateTime.utc_now() |> DateTime.to_iso8601() |> String.slice(0, 17) # YYYYMMDD-HH:MM:SS + str_data = "8=FIXT.1.1|9=131|35=8|34=12345|49=SELL|52=#{sending_time}.000|" <> + "56=BUY|1=531|11=99|14=5|17=872|31=1.2|32=5|37=456|38=5|39=2|54=1|55=ABC|" <> + "150=F|151=0|10=240|" + :binary.replace(str_data, "|", << 1 >>, [:global]) + end + def get_serialize_data() do out_message = "D" |> OutMessage.new() diff --git a/lib/ex_fix/parser.ex b/lib/ex_fix/parser.ex index 2167c05..709d7db 100644 --- a/lib/ex_fix/parser.ex +++ b/lib/ex_fix/parser.ex @@ -13,8 +13,8 @@ defmodule ExFix.Parser do @doc """ Parse full message """ - def parse(data, dictionary, expected_seqnum \\ nil, validate \\ true) do - with %InMessage{valid: true} = msg1 <- parse1(data, dictionary, expected_seqnum, validate), + def parse(data, dictionary, expected_seqnum \\ nil, validate \\ true, validate_sending_time \\ true) do + with %InMessage{valid: true} = msg1 <- parse1(data, dictionary, expected_seqnum, validate, validate_sending_time), msg2 <- parse2(msg1) do msg2 end @@ -23,9 +23,9 @@ defmodule ExFix.Parser do @doc """ Parse - stage1 """ - def parse1(data, dictionary, expected_seqnum \\ nil, validate \\ true) + def parse1(data, dictionary, expected_seqnum \\ nil, validate \\ true, validate_sending_time \\ true) - def parse1(<<"8=FIXT.1.1", @soh, "9=", rest::binary>>, dictionary, expected_seqnum, validate) do + def parse1(<<"8=FIXT.1.1", @soh, "9=", rest::binary>>, dictionary, expected_seqnum, validate, _validate_sending_time) do [str_len, rest1] = :binary.split(rest, <<@soh>>) {len, _} = Integer.parse(str_len) @@ -64,7 +64,7 @@ defmodule ExFix.Parser do end end - def parse1(<<"8=", _rest::binary>> = orig_msg, _dictionary, _expected_seqnum, _validate) do + def parse1(<<"8=", _rest::binary>> = orig_msg, _dictionary, _expected_seqnum, _validate, _validate_sending_time) do %InMessage{ valid: false, msg_type: nil, @@ -75,7 +75,7 @@ defmodule ExFix.Parser do } end - def parse1(data, _dictionary, _expected_seqnum, _validate) do + def parse1(data, _dictionary, _expected_seqnum, _validate, _validate_sending_time) do %InMessage{ valid: false, msg_type: nil, diff --git a/lib/ex_fix/session.ex b/lib/ex_fix/session.ex index 1e923f0..a4d7803 100644 --- a/lib/ex_fix/session.ex +++ b/lib/ex_fix/session.ex @@ -224,6 +224,7 @@ defmodule ExFix.Session do config: %SessionConfig{ name: session_name, validate_incoming_message: validate, + validate_sending_time: validate_sending_time, log_incoming_msg: log_incoming_msg, dictionary: dictionary }, @@ -239,7 +240,8 @@ defmodule ExFix.Session do <>, dictionary, expected_seqnum, - validate + validate, + validate_sending_time ) case msg.valid do @@ -719,6 +721,10 @@ defmodule ExFix.Session do |> binary_part(0, len) end + defp validate_sending_time(_session_name, %Session{config: %SessionConfig{validate_sending_time: false}}, _msg, _expected_seqnum) do + :ok + end + defp validate_sending_time(session_name, %Session{config: config, out_lastseq: out_lastseq} = session, %InMessage{fields: fields, other_msgs: other}, expected_seqnum) do %SessionConfig{time_service: time_service, session_handler: handler, env: env} = config diff --git a/lib/ex_fix/session_config.ex b/lib/ex_fix/session_config.ex index 585ffa9..0256d24 100644 --- a/lib/ex_fix/session_config.ex +++ b/lib/ex_fix/session_config.ex @@ -23,6 +23,7 @@ defmodule ExFix.SessionConfig do reconnect_interval: 15, reset_on_logon: true, validate_incoming_message: true, + validate_sending_time: true, transport_mod: :gen_tcp, transport_options: [], time_service: nil,