Skip to content

Commit

Permalink
Read message type from fix string without regex
Browse files Browse the repository at this point in the history
BenchmarkDotNet v0.14.0, NixOS 24.11 (Vicuna)
    11th Gen Intel Core i7-1165G7 2.80GHz, 1 CPU, 8 logical and 4 physical cores
    .NET SDK 8.0.307
      [Host]     : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
      DefaultJob : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI

    | Method | Mean      | Error    | StdDev    | Gen0   | Allocated |
    |------- |----------:|---------:|----------:|-------:|----------:|
    | Manual |  35.33 ns | 2.754 ns |  8.122 ns | 0.0038 |      24 B |
    | Regex  | 322.38 ns | 9.405 ns | 25.265 ns | 0.0648 |     408 B |
  • Loading branch information
jkulubya authored and gbirchmeier committed Dec 17, 2024
1 parent 72facb6 commit ee47ce9
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 15 deletions.
29 changes: 24 additions & 5 deletions QuickFIXn/Message/Message.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Text;
using QuickFix.Fields;
using System.Text.RegularExpressions;
using System.Text.Json;
using System.Collections.Generic;
using QuickFix.DataDictionary;
Expand Down Expand Up @@ -256,11 +255,31 @@ public static SessionID GetReverseSessionId(string msg)
/// <exception cref="MessageParseError">if 35 tag is missing or malformed</exception>
public static string GetMsgType(string fixstring)
{
Match match = Regex.Match(fixstring, SOH + "35=([^" + SOH + "]*)" + SOH);
if (match.Success)
return match.Groups[1].Value;
// Note: This is faster than regex. See stats from https://github.com/connamara/quickfixn/pull/910
ReadOnlySpan<char> chars = fixstring.AsSpan();
int l = 0;
int r = 1;

throw new MessageParseError("missing or malformed tag 35 in msg: " + fixstring);
if (chars.Length > 0 && chars[0] == SOH) l = 1;
while (r < chars.Length)
{
if (chars[r] == SOH)
{
if (r - l >= 4 &&
chars[l] == '3' &&
chars[l + 1] == '5' &&
chars[l + 2] == '=')
{
return new string(chars[(l + 3)..r]);
}

l = r + 1;
}

r++;
}

throw new MessageParseError("Missing or malformed tag 35 in msg: " + fixstring);
}

public static ApplVerID GetApplVerID(string beginString)
Expand Down
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ What's New
* #891 - make NonSessionLog implement IDisposable and fix the IOException (VAllens)
* #893 - Upgrade the unit testing framework to the latest version and remove obsolete Assert methods (VAllens)
* #907 - A fix to make body-less messages work, specifically 35=n aka XMLnonFIX (gbirchmeier)
* #910 - faster Message.GetMsgType that doesn't use Regex (jkulubya)

### v1.12.0

Expand Down
27 changes: 17 additions & 10 deletions UnitTests/MessageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -580,16 +580,23 @@ public void TestGetSetSessionId()
Assert.That(getSessionId.TargetLocationID, Is.EqualTo(""));
}

[Test]
public void GetMsgTypeTest() {
string msgStr = ("8=FIX.4.4|9=104|35=W|34=3|49=sender|52=20110909-09:09:09.999|56=target"
+ "55=sym|268=1|269=0|272=20111012|273=22:15:30.444|10=19|").Replace('|', Message.SOH);
Assert.That(Message.GetMsgType(msgStr), Is.EqualTo("W"));

// invalid 35 value, let it ride
string msgStr2 = ("8=FIX.4.4|9=68|35=*|34=3|49=sender|52=20110909-09:09:09.999|56=target"
+ "55=sym|268=0|10=9|").Replace('|', Message.SOH);
Assert.That(Message.GetMsgType(msgStr2), Is.EqualTo("*"));
[TestCase("8=FIX.4.4|9=104|35=W|34=3|49=sender|52=20110909-09:09:09.999|56=target|55=sym|268=1|269=0|272=20111012|273=22:15:30.444|10=19|", "W")]
[TestCase("8=FIX.4.4|9=104|35=AW|34=3|49=sender|52=20110909-09:09:09.999|56=target|55=sym|268=1|269=0|272=20111012|273=22:15:30.444|10=19|", "AW")]
[TestCase("8=FIX.4.4|9=68|35=*|34=3|49=sender|52=20110909-09:09:09.999|56=target|55=sym|268=0|10=9|", "*")]
public void GetMsgTypeTest(string message, string expectedMessageType)
{
var msgStr = message.Replace('|', Message.SOH);
Assert.That(Message.GetMsgType(msgStr), Is.EqualTo(expectedMessageType));
}

[TestCase("")]
[TestCase("8=FIX.4.4|9=68|34=3|49=sender|52=20110909-09:09:09.999|56=target|55=sym|268=0|10=9|")]
[TestCase("8=FIX.4.4|9=68|35=|34=3|49=sender|52=20110909-09:09:09.999|56=target|55=sym|268=0|10=9|")]
[TestCase("8=FIX.4.4|9=68|35=")]
public void GetInvalidMsgTypeTest(string message)
{
var msgStr = message.Replace('|', Message.SOH);
Assert.Throws<MessageParseError>(() => Message.GetMsgType(msgStr));
}

[Test]
Expand Down

0 comments on commit ee47ce9

Please sign in to comment.