diff --git a/QuickFIXn/Dictionary.cs b/QuickFIXn/Dictionary.cs index ae3753a07..c8c45bf55 100755 --- a/QuickFIXn/Dictionary.cs +++ b/QuickFIXn/Dictionary.cs @@ -105,6 +105,22 @@ public long GetLong(string key) } } + public ulong GetULong(string key) + { + try + { + return Convert.ToUInt64(GetString(key)); + } + catch(FormatException) + { + throw new ConfigError("Incorrect data type"); + } + catch(QuickFIXException) + { + throw new ConfigError("No value for key: " + key); + } + } + public double GetDouble(string key) { try diff --git a/QuickFIXn/Fields/Converters/AsciiConverter.cs b/QuickFIXn/Fields/Converters/AsciiConverter.cs new file mode 100644 index 000000000..a798ef93f --- /dev/null +++ b/QuickFIXn/Fields/Converters/AsciiConverter.cs @@ -0,0 +1,29 @@ + +namespace QuickFix.Fields.Converters +{ + /// + /// convert Int64 to/from string + /// + public static class AsciiValidator + { + public const int ASCII_ZERO = 48; + public const int ASCII_NINE = 57; + public const int ASCII_MINUS = 45; + + /// + /// TODO can we use NumberFormatInfo or NumberStyles to avoid this bit of ASCII hackery? + /// Validates that a string looks like number (for use before conversion to an int, ulong, etc.). + /// + /// + /// + public static void Validate(string i) + { + if ((null == i) || (i.Length < 1)) + throw new FieldConvertError("The argument string cannot be null or empty"); + int asciiValOfFirstChar = System.Convert.ToInt32(i[0]); + if ((asciiValOfFirstChar < ASCII_ZERO) || (asciiValOfFirstChar > ASCII_NINE)) + if (asciiValOfFirstChar != ASCII_MINUS) + throw new FieldConvertError("Could not convert string to int (" + i + "): The first character must be a digit or a minus sign"); + } + } +} diff --git a/QuickFIXn/Fields/Converters/DecimalConverter.cs b/QuickFIXn/Fields/Converters/DecimalConverter.cs index 550d9fa85..749fa180f 100644 --- a/QuickFIXn/Fields/Converters/DecimalConverter.cs +++ b/QuickFIXn/Fields/Converters/DecimalConverter.cs @@ -23,8 +23,8 @@ public static Decimal Convert(string d) if((null == d) || (d.Length < 1)) throw new FieldConvertError("The argument string cannot be null or empty"); int asciiValOfFirstChar = System.Convert.ToInt32(d[0]); - if ((asciiValOfFirstChar < IntConverter.ASCII_ZERO) || (asciiValOfFirstChar > IntConverter.ASCII_NINE)) - if (asciiValOfFirstChar != IntConverter.ASCII_MINUS && asciiValOfFirstChar != ASCII_DECIMALPOINT) + if ((asciiValOfFirstChar < AsciiValidator.ASCII_ZERO) || (asciiValOfFirstChar > AsciiValidator.ASCII_NINE)) + if (asciiValOfFirstChar != AsciiValidator.ASCII_MINUS && asciiValOfFirstChar != ASCII_DECIMALPOINT) throw new FieldConvertError("Could not convert string to decimal (" + d + "): The first character must be a digit, decimal point, or minus sign"); return decimal.Parse(d, System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowLeadingSign | System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.CultureInfo.InvariantCulture); } diff --git a/QuickFIXn/Fields/Converters/IntConverter.cs b/QuickFIXn/Fields/Converters/IntConverter.cs index 261fb0d8c..bb38bdb42 100644 --- a/QuickFIXn/Fields/Converters/IntConverter.cs +++ b/QuickFIXn/Fields/Converters/IntConverter.cs @@ -6,12 +6,7 @@ namespace QuickFix.Fields.Converters /// public static class IntConverter { - public const int ASCII_ZERO = 48; - public const int ASCII_NINE = 57; - public const int ASCII_MINUS = 45; - /// - /// TODO can we use NumberFormatInfo or NumberStyles to avoid this bit of ASCII hackery? /// Converts string to int. /// /// @@ -20,12 +15,7 @@ public static int Convert(string i) { try { - if ((null == i) || (i.Length < 1)) - throw new FieldConvertError("The argument string cannot be null or empty"); - int asciiValOfFirstChar = System.Convert.ToInt32(i[0]); - if ((asciiValOfFirstChar < ASCII_ZERO) || (asciiValOfFirstChar > ASCII_NINE)) - if (asciiValOfFirstChar != ASCII_MINUS) - throw new FieldConvertError("Could not convert string to int (" + i + "): The first character must be a digit or a minus sign"); + AsciiValidator.Validate(i); return System.Convert.ToInt32(i); } catch (System.FormatException e) @@ -34,7 +24,7 @@ public static int Convert(string i) } catch (System.OverflowException e) { - throw new FieldConvertError("Could not convert string to int(" + i + ")", e); + throw new FieldConvertError("Could not convert string to int (" + i + ")", e); } } diff --git a/QuickFIXn/Fields/Converters/ULongConverter.cs b/QuickFIXn/Fields/Converters/ULongConverter.cs new file mode 100644 index 000000000..efc4a7f42 --- /dev/null +++ b/QuickFIXn/Fields/Converters/ULongConverter.cs @@ -0,0 +1,39 @@ + +namespace QuickFix.Fields.Converters +{ + /// + /// convert UInt64 to/from string + /// + public static class ULongConverter + { + /// + /// Converts string to ulong. + /// + /// + /// + public static ulong Convert(string i) + { + try + { + AsciiValidator.Validate(i); + return System.Convert.ToUInt64(i); + } + catch (System.FormatException e) + { + throw new FieldConvertError("Could not convert string to ulong (" + i + ")", e); + } + catch (System.OverflowException e) + { + throw new FieldConvertError("Could not convert string to ulong (" + i + ")", e); + } + } + + /// + /// convert ulong to string + /// + public static string Convert(System.UInt64 i) + { + return i.ToString(); + } + } +} diff --git a/QuickFIXn/Fields/Fields.cs b/QuickFIXn/Fields/Fields.cs index 1da11bc56..a1f7ee6af 100644 --- a/QuickFIXn/Fields/Fields.cs +++ b/QuickFIXn/Fields/Fields.cs @@ -108,13 +108,13 @@ public AvgPx(Decimal val) /// /// BeginSeqNo Field /// / - public sealed class BeginSeqNo : IntField + public sealed class BeginSeqNo : SeqNumFieldType { public const int TAG = 7; public BeginSeqNo() :base(Tags.BeginSeqNo) {} - public BeginSeqNo(int val) + public BeginSeqNo(SeqNumType val) :base(Tags.BeginSeqNo, val) {} } @@ -257,13 +257,13 @@ public Currency(string val) /// /// EndSeqNo Field /// / - public sealed class EndSeqNo : IntField + public sealed class EndSeqNo : SeqNumFieldType { public const int TAG = 16; public EndSeqNo() :base(Tags.EndSeqNo) {} - public EndSeqNo(int val) + public EndSeqNo(SeqNumType val) :base(Tags.EndSeqNo, val) {} } @@ -637,13 +637,13 @@ public LinesOfText(int val) /// /// MsgSeqNum Field /// / - public sealed class MsgSeqNum : IntField + public sealed class MsgSeqNum : SeqNumFieldType { public const int TAG = 34; public MsgSeqNum() :base(Tags.MsgSeqNum) {} - public MsgSeqNum(int val) + public MsgSeqNum(SeqNumType val) :base(Tags.MsgSeqNum, val) {} } @@ -664,16 +664,112 @@ public MsgType(string val) // Field Enumerations public const string HEARTBEAT = "0"; + public const string TEST_REQUEST = "1"; + public const string RESEND_REQUEST = "2"; + public const string REJECT = "3"; + public const string SEQUENCE_RESET = "4"; + public const string LOGOUT = "5"; + public const string INDICATION_OF_INTEREST = "6"; + public const string ADVERTISEMENT = "7"; + public const string EXECUTION_REPORT = "8"; + public const string ORDER_CANCEL_REJECT = "9"; + public const string LOGON = "A"; + public const string NEWS = "B"; + public const string EMAIL = "C"; + public const string ORDER_SINGLE = "D"; + public const string ORDER_LIST = "E"; + public const string ORDER_CANCEL_REQUEST = "F"; + public const string ORDER_CANCEL_REPLACE_REQUEST = "G"; + public const string ORDER_STATUS_REQUEST = "H"; + public const string ALLOCATION_INSTRUCTION = "J"; + public const string LIST_CANCEL_REQUEST = "K"; + public const string LIST_EXECUTE = "L"; + public const string LIST_STATUS_REQUEST = "M"; + public const string LIST_STATUS = "N"; + public const string ALLOCATION_INSTRUCTION_ACK = "P"; + public const string DONT_KNOW_TRADE = "Q"; + public const string QUOTE_REQUEST = "R"; + public const string QUOTE = "S"; + public const string SETTLEMENT_INSTRUCTIONS = "T"; + public const string MARKET_DATA_REQUEST = "V"; + public const string MARKET_DATA_SNAPSHOT_FULL_REFRESH = "W"; + public const string MARKET_DATA_INCREMENTAL_REFRESH = "X"; + public const string MARKET_DATA_REQUEST_REJECT = "Y"; + public const string QUOTE_CANCEL = "Z"; + public const string QUOTE_STATUS_REQUEST = "a"; + public const string MASS_QUOTE_ACKNOWLEDGEMENT = "b"; + public const string SECURITY_DEFINITION_REQUEST = "c"; + public const string SECURITY_DEFINITION = "d"; + public const string SECURITY_STATUS_REQUEST = "e"; + public const string SECURITY_STATUS = "f"; + public const string TRADING_SESSION_STATUS_REQUEST = "g"; + public const string TRADING_SESSION_STATUS = "h"; + public const string MASS_QUOTE = "i"; + public const string BUSINESS_MESSAGE_REJECT = "j"; + public const string BID_REQUEST = "k"; + public const string BID_RESPONSE = "l"; + public const string LIST_STRIKE_PRICE = "m"; + public const string XML_MESSAGE = "n"; + public const string REGISTRATION_INSTRUCTIONS = "o"; + public const string REGISTRATION_INSTRUCTIONS_RESPONSE = "p"; + public const string ORDER_MASS_CANCEL_REQUEST = "q"; + public const string ORDER_MASS_CANCEL_REPORT = "r"; + public const string NEW_ORDER_CROSS = "s"; + public const string CROSS_ORDER_CANCEL_REPLACE_REQUEST = "t"; + public const string CROSS_ORDER_CANCEL_REQUEST = "u"; + public const string SECURITY_TYPE_REQUEST = "v"; + public const string SECURITY_TYPES = "w"; + public const string SECURITY_LIST_REQUEST = "x"; + public const string SECURITY_LIST = "y"; + public const string DERIVATIVE_SECURITY_LIST_REQUEST = "z"; + public const string DERIVATIVE_SECURITY_LIST = "AA"; + public const string NEW_ORDER_MULTILEG = "AB"; + public const string MULTILEG_ORDER_CANCEL_REPLACE = "AC"; + public const string TRADE_CAPTURE_REPORT_REQUEST = "AD"; + public const string TRADE_CAPTURE_REPORT = "AE"; + public const string ORDER_MASS_STATUS_REQUEST = "AF"; + public const string QUOTE_REQUEST_REJECT = "AG"; + public const string RFQ_REQUEST = "AH"; + public const string QUOTE_STATUS_REPORT = "AI"; + public const string QUOTE_RESPONSE = "AJ"; + public const string CONFIRMATION = "AK"; + public const string POSITION_MAINTENANCE_REQUEST = "AL"; + public const string POSITION_MAINTENANCE_REPORT = "AM"; + public const string REQUEST_FOR_POSITIONS = "AN"; + public const string REQUEST_FOR_POSITIONS_ACK = "AO"; + public const string POSITION_REPORT = "AP"; + public const string TRADE_CAPTURE_REPORT_REQUEST_ACK = "AQ"; + public const string TRADE_CAPTURE_REPORT_ACK = "AR"; + public const string ALLOCATION_REPORT = "AS"; + public const string ALLOCATION_REPORT_ACK = "AT"; + public const string CONFIRMATION_ACK = "AU"; + public const string SETTLEMENT_INSTRUCTION_REQUEST = "AV"; + public const string ASSIGNMENT_REPORT = "AW"; + public const string COLLATERAL_REQUEST = "AX"; + public const string COLLATERAL_ASSIGNMENT = "AY"; + public const string COLLATERAL_RESPONSE = "AZ"; + public const string COLLATERAL_REPORT = "BA"; + public const string COLLATERAL_INQUIRY = "BB"; + public const string NETWORK_STATUS_REQUEST = "BC"; + public const string NETWORK_STATUS_RESPONSE = "BD"; + public const string USER_REQUEST = "BE"; + public const string USER_RESPONSE = "BF"; + public const string COLLATERAL_INQUIRY_ACK = "BG"; + public const string CONFIRMATION_REQUEST = "BH"; + public const string TRADING_SESSION_LIST_REQUEST = "BI"; + public const string TRADING_SESSION_LIST = "BJ"; + public const string SECURITY_LIST_UPDATE_REPORT = "BK"; + public const string ADJUSTED_POSITION_REPORT = "BL"; + public const string ALLOCATION_INSTRUCTION_ALERT = "BM"; + public const string EXECUTION_ACKNOWLEDGEMENT = "BN"; + public const string CONTRARY_INTENTION_REPORT = "BO"; + public const string SECURITY_DEFINITION_UPDATE_REPORT = "BP"; public const string TESTREQUEST = "1"; public const string RESENDREQUEST = "2"; - public const string REJECT = "3"; public const string SEQUENCERESET = "4"; - public const string LOGOUT = "5"; public const string IOI = "6"; - public const string ADVERTISEMENT = "7"; public const string EXECUTIONREPORT = "8"; public const string ORDERCANCELREJECT = "9"; - public const string LOGON = "A"; public const string DERIVATIVESECURITYLIST = "AA"; public const string NEWORDERMULTILEG = "AB"; public const string MULTILEGORDERCANCELREPLACE = "AC"; @@ -684,7 +780,6 @@ public MsgType(string val) public const string RFQREQUEST = "AH"; public const string QUOTESTATUSREPORT = "AI"; public const string QUOTERESPONSE = "AJ"; - public const string CONFIRMATION = "AK"; public const string POSITIONMAINTENANCEREQUEST = "AL"; public const string POSITIONMAINTENANCEREPORT = "AM"; public const string REQUESTFORPOSITIONS = "AN"; @@ -694,13 +789,11 @@ public MsgType(string val) public const string TRADECAPTUREREPORTACK = "AR"; public const string ALLOCATIONREPORT = "AS"; public const string ALLOCATIONREPORTACK = "AT"; - public const string CONFIRMATION_ACK = "AU"; public const string SETTLEMENTINSTRUCTIONREQUEST = "AV"; public const string ASSIGNMENTREPORT = "AW"; public const string COLLATERALREQUEST = "AX"; public const string COLLATERALASSIGNMENT = "AY"; public const string COLLATERALRESPONSE = "AZ"; - public const string NEWS = "B"; public const string COLLATERALREPORT = "BA"; public const string COLLATERALINQUIRY = "BB"; public const string NETWORKCOUNTERPARTYSYSTEMSTATUSREQUEST = "BC"; @@ -727,7 +820,6 @@ public MsgType(string val) public const string APPLICATIONMESSAGEREQUESTACK = "BX"; public const string APPLICATIONMESSAGEREPORT = "BY"; public const string ORDERMASSACTIONREPORT = "BZ"; - public const string EMAIL = "C"; public const string ORDERMASSACTIONREQUEST = "CA"; public const string USERNOTIFICATION = "CB"; public const string STREAMASSIGNMENTREQUEST = "CC"; @@ -748,7 +840,6 @@ public MsgType(string val) public const string ALLOCATIONINSTRUCTIONACK = "P"; public const string DONTKNOWTRADEDK = "Q"; public const string QUOTEREQUEST = "R"; - public const string QUOTE = "S"; public const string SETTLEMENTINSTRUCTIONS = "T"; public const string MARKETDATAREQUEST = "V"; public const string MARKETDATASNAPSHOTFULLREFRESH = "W"; @@ -781,105 +872,14 @@ public MsgType(string val) public const string SECURITYLISTREQUEST = "x"; public const string SECURITYLIST = "y"; public const string DERIVATIVESECURITYLISTREQUEST = "z"; - public const string TEST_REQUEST = "1"; - public const string RESEND_REQUEST = "2"; - public const string SEQUENCE_RESET = "4"; - public const string INDICATION_OF_INTEREST = "6"; - public const string EXECUTION_REPORT = "8"; - public const string ORDER_CANCEL_REJECT = "9"; - public const string QUOTE_STATUS_REQUEST = "a"; - public const string DERIVATIVE_SECURITY_LIST = "AA"; public const string NEW_ORDER_AB = "AB"; - public const string MULTILEG_ORDER_CANCEL_REPLACE = "AC"; - public const string TRADE_CAPTURE_REPORT_REQUEST = "AD"; - public const string TRADE_CAPTURE_REPORT = "AE"; - public const string ORDER_MASS_STATUS_REQUEST = "AF"; - public const string QUOTE_REQUEST_REJECT = "AG"; - public const string RFQ_REQUEST = "AH"; - public const string QUOTE_STATUS_REPORT = "AI"; - public const string QUOTE_RESPONSE = "AJ"; - public const string POSITION_MAINTENANCE_REQUEST = "AL"; - public const string POSITION_MAINTENANCE_REPORT = "AM"; - public const string REQUEST_FOR_POSITIONS = "AN"; - public const string REQUEST_FOR_POSITIONS_ACK = "AO"; - public const string POSITION_REPORT = "AP"; - public const string TRADE_CAPTURE_REPORT_REQUEST_ACK = "AQ"; - public const string TRADE_CAPTURE_REPORT_ACK = "AR"; - public const string ALLOCATION_REPORT = "AS"; - public const string ALLOCATION_REPORT_ACK = "AT"; - public const string SETTLEMENT_INSTRUCTION_REQUEST = "AV"; - public const string ASSIGNMENT_REPORT = "AW"; - public const string COLLATERAL_REQUEST = "AX"; - public const string COLLATERAL_ASSIGNMENT = "AY"; - public const string COLLATERAL_RESPONSE = "AZ"; - public const string MASS_QUOTE_ACKNOWLEDGEMENT = "b"; - public const string COLLATERAL_REPORT = "BA"; - public const string COLLATERAL_INQUIRY = "BB"; public const string NETWORK_COUNTERPARTY_SYSTEM_STATUS_REQUEST = "BC"; public const string NETWORK_COUNTERPARTY_SYSTEM_STATUS_RESPONSE = "BD"; - public const string USER_REQUEST = "BE"; - public const string USER_RESPONSE = "BF"; - public const string COLLATERAL_INQUIRY_ACK = "BG"; - public const string CONFIRMATION_REQUEST = "BH"; - public const string SECURITY_DEFINITION_REQUEST = "c"; - public const string SECURITY_DEFINITION = "d"; public const string NEW_ORDER_D = "D"; - public const string SECURITY_STATUS_REQUEST = "e"; public const string NEW_ORDER_E = "E"; - public const string ORDER_CANCEL_REQUEST = "F"; - public const string SECURITY_STATUS = "f"; - public const string ORDER_CANCEL_REPLACE_REQUEST = "G"; - public const string TRADING_SESSION_STATUS_REQUEST = "g"; - public const string ORDER_STATUS_REQUEST = "H"; - public const string TRADING_SESSION_STATUS = "h"; - public const string MASS_QUOTE = "i"; - public const string BUSINESS_MESSAGE_REJECT = "j"; - public const string ALLOCATION_INSTRUCTION = "J"; - public const string BID_REQUEST = "k"; - public const string LIST_CANCEL_REQUEST = "K"; - public const string BID_RESPONSE = "l"; - public const string LIST_EXECUTE = "L"; - public const string LIST_STRIKE_PRICE = "m"; - public const string LIST_STATUS_REQUEST = "M"; - public const string XML_MESSAGE = "n"; - public const string LIST_STATUS = "N"; - public const string REGISTRATION_INSTRUCTIONS = "o"; - public const string REGISTRATION_INSTRUCTIONS_RESPONSE = "p"; - public const string ALLOCATION_INSTRUCTION_ACK = "P"; - public const string ORDER_MASS_CANCEL_REQUEST = "q"; - public const string DONT_KNOW_TRADE = "Q"; - public const string QUOTE_REQUEST = "R"; - public const string ORDER_MASS_CANCEL_REPORT = "r"; public const string NEW_ORDER_s = "s"; - public const string SETTLEMENT_INSTRUCTIONS = "T"; - public const string CROSS_ORDER_CANCEL_REPLACE_REQUEST = "t"; - public const string CROSS_ORDER_CANCEL_REQUEST = "u"; - public const string MARKET_DATA_REQUEST = "V"; - public const string SECURITY_TYPE_REQUEST = "v"; - public const string SECURITY_TYPES = "w"; public const string MARKET_DATA_W = "W"; - public const string SECURITY_LIST_REQUEST = "x"; public const string MARKET_DATA_X = "X"; - public const string MARKET_DATA_REQUEST_REJECT = "Y"; - public const string SECURITY_LIST = "y"; - public const string QUOTE_CANCEL = "Z"; - public const string DERIVATIVE_SECURITY_LIST_REQUEST = "z"; - public const string CONTRARY_INTENTION_REPORT = "BO"; - public const string SECURITY_DEFINITION_UPDATE_REPORT = "BP"; - public const string SECURITY_LIST_UPDATE_REPORT = "BK"; - public const string ADJUSTED_POSITION_REPORT = "BL"; - public const string ALLOCATION_INSTRUCTION_ALERT = "BM"; - public const string EXECUTION_ACKNOWLEDGEMENT = "BN"; - public const string TRADING_SESSION_LIST = "BJ"; - public const string TRADING_SESSION_LIST_REQUEST = "BI"; - public const string ORDER_SINGLE = "D"; - public const string ORDER_LIST = "E"; - public const string MARKET_DATA_SNAPSHOT_FULL_REFRESH = "W"; - public const string MARKET_DATA_INCREMENTAL_REFRESH = "X"; - public const string NEW_ORDER_CROSS = "s"; - public const string NEW_ORDER_MULTILEG = "AB"; - public const string NETWORK_STATUS_REQUEST = "BC"; - public const string NETWORK_STATUS_RESPONSE = "BD"; public const string ORDER_CANCEL = "G"; public const string ALLOCATION = "J"; public const string ALLOCATION_ACK = "P"; @@ -892,13 +892,13 @@ public MsgType(string val) /// /// NewSeqNo Field /// / - public sealed class NewSeqNo : IntField + public sealed class NewSeqNo : SeqNumFieldType { public const int TAG = 36; public NewSeqNo() :base(Tags.NewSeqNo) {} - public NewSeqNo(int val) + public NewSeqNo(SeqNumType val) :base(Tags.NewSeqNo, val) {} } @@ -1083,13 +1083,13 @@ public Price(Decimal val) /// /// RefSeqNum Field /// / - public sealed class RefSeqNum : IntField + public sealed class RefSeqNum : SeqNumFieldType { public const int TAG = 45; public RefSeqNum() :base(Tags.RefSeqNum) {} - public RefSeqNum(int val) + public RefSeqNum(SeqNumType val) :base(Tags.RefSeqNum, val) {} } @@ -2067,19 +2067,19 @@ public EncryptMethod(int val) // Field Enumerations - public const int NONE = 0; - public const int PKCS_1 = 1; - public const int DES = 2; - public const int PKCS_3 = 3; - public const int PGP_4 = 4; - public const int PGP_5 = 5; - public const int PEM = 6; public const int NONE_OTHER = 0; public const int PKCS = 1; + public const int DES = 2; public const int PKCS_DES = 3; public const int PGP_DES = 4; public const int PGP_DES_MD5 = 5; public const int PEM_DES_MD5 = 6; + public const int NONE = 0; + public const int PKCS_1 = 1; + public const int PKCS_3 = 3; + public const int PGP_4 = 4; + public const int PGP_5 = 5; + public const int PEM = 6; public const int PKCS_PROPRIETARY = 1; public const int PKCSDES = 3; public const int PGPDES = 4; @@ -6454,13 +6454,13 @@ public QuoteEntryRejectReason(int val) /// /// LastMsgSeqNumProcessed Field /// / - public sealed class LastMsgSeqNumProcessed : IntField + public sealed class LastMsgSeqNumProcessed : SeqNumFieldType { public const int TAG = 369; public LastMsgSeqNumProcessed() :base(Tags.LastMsgSeqNumProcessed) {} - public LastMsgSeqNumProcessed(int val) + public LastMsgSeqNumProcessed(SeqNumType val) :base(Tags.LastMsgSeqNumProcessed, val) {} } @@ -12221,13 +12221,13 @@ public HopSendingTime(DateTime val, Converters.TimeStampPrecision precision) /// /// HopRefID Field /// / - public sealed class HopRefID : IntField + public sealed class HopRefID : SeqNumFieldType { public const int TAG = 630; public HopRefID() :base(Tags.HopRefID) {} - public HopRefID(int val) + public HopRefID(SeqNumType val) :base(Tags.HopRefID, val) {} } @@ -15184,13 +15184,13 @@ public TerminationType(int val) /// /// NextExpectedMsgSeqNum Field /// / - public sealed class NextExpectedMsgSeqNum : IntField + public sealed class NextExpectedMsgSeqNum : SeqNumFieldType { public const int TAG = 789; public NextExpectedMsgSeqNum() :base(Tags.NextExpectedMsgSeqNum) {} - public NextExpectedMsgSeqNum(int val) + public NextExpectedMsgSeqNum(SeqNumType val) :base(Tags.NextExpectedMsgSeqNum, val) {} } @@ -21987,13 +21987,13 @@ public ApplID(string val) /// /// ApplSeqNum Field /// / - public sealed class ApplSeqNum : IntField + public sealed class ApplSeqNum : SeqNumFieldType { public const int TAG = 1181; public ApplSeqNum() :base(Tags.ApplSeqNum) {} - public ApplSeqNum(int val) + public ApplSeqNum(SeqNumType val) :base(Tags.ApplSeqNum, val) {} } @@ -22002,13 +22002,13 @@ public ApplSeqNum(int val) /// /// ApplBegSeqNum Field /// / - public sealed class ApplBegSeqNum : IntField + public sealed class ApplBegSeqNum : SeqNumFieldType { public const int TAG = 1182; public ApplBegSeqNum() :base(Tags.ApplBegSeqNum) {} - public ApplBegSeqNum(int val) + public ApplBegSeqNum(SeqNumType val) :base(Tags.ApplBegSeqNum, val) {} } @@ -22017,13 +22017,13 @@ public ApplBegSeqNum(int val) /// /// ApplEndSeqNum Field /// / - public sealed class ApplEndSeqNum : IntField + public sealed class ApplEndSeqNum : SeqNumFieldType { public const int TAG = 1183; public ApplEndSeqNum() :base(Tags.ApplEndSeqNum) {} - public ApplEndSeqNum(int val) + public ApplEndSeqNum(SeqNumType val) :base(Tags.ApplEndSeqNum, val) {} } @@ -24568,13 +24568,13 @@ public ApplTotalMessageCount(int val) /// /// ApplLastSeqNum Field /// / - public sealed class ApplLastSeqNum : IntField + public sealed class ApplLastSeqNum : SeqNumFieldType { public const int TAG = 1350; public ApplLastSeqNum() :base(Tags.ApplLastSeqNum) {} - public ApplLastSeqNum(int val) + public ApplLastSeqNum(SeqNumType val) :base(Tags.ApplLastSeqNum, val) {} } @@ -24678,13 +24678,13 @@ public ApplReportID(string val) /// /// RefApplLastSeqNum Field /// / - public sealed class RefApplLastSeqNum : IntField + public sealed class RefApplLastSeqNum : SeqNumFieldType { public const int TAG = 1357; public RefApplLastSeqNum() :base(Tags.RefApplLastSeqNum) {} - public RefApplLastSeqNum(int val) + public RefApplLastSeqNum(SeqNumType val) :base(Tags.RefApplLastSeqNum, val) {} } @@ -25390,13 +25390,13 @@ public EncodedMktSegmDesc(string val) /// /// ApplNewSeqNum Field /// / - public sealed class ApplNewSeqNum : IntField + public sealed class ApplNewSeqNum : SeqNumFieldType { public const int TAG = 1399; public ApplNewSeqNum() :base(Tags.ApplNewSeqNum) {} - public ApplNewSeqNum(int val) + public ApplNewSeqNum(SeqNumType val) :base(Tags.ApplNewSeqNum, val) {} } diff --git a/QuickFIXn/Fields/ULongField.cs b/QuickFIXn/Fields/ULongField.cs new file mode 100644 index 000000000..6a30c7ad7 --- /dev/null +++ b/QuickFIXn/Fields/ULongField.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace QuickFix.Fields +{ + /// + /// A ulong (System.UInt64) message field + /// + public class ULongField: FieldBase + { + public ULongField(int tag) + : base(tag, 0L) { } + + public ULongField(int tag, ulong val) + : base(tag, val) {} + + // quickfix compat + public ulong getValue() + { return Obj; } + + public void setValue(ulong v) + { Obj = v; } + + protected override string makeString() + { + return Converters.ULongConverter.Convert(Obj); + } + } +} diff --git a/QuickFIXn/FileStore.cs b/QuickFIXn/FileStore.cs index 47e2a3216..afb221af7 100755 --- a/QuickFIXn/FileStore.cs +++ b/QuickFIXn/FileStore.cs @@ -33,7 +33,7 @@ public MsgDef(long index, int size) private MemoryStore cache_ = new MemoryStore(); - System.Collections.Generic.Dictionary offsets_ = new Dictionary(); + System.Collections.Generic.Dictionary offsets_ = new Dictionary(); public static string Prefix(SessionID sessionID) { @@ -132,7 +132,7 @@ private void ConstructFromFileCache() string[] headerParts = line.Split(','); if (headerParts.Length == 3) { - offsets_[Convert.ToInt32(headerParts[0])] = new MsgDef( + offsets_[Convert.ToUInt64(headerParts[0])] = new MsgDef( Convert.ToInt64(headerParts[1]), Convert.ToInt32(headerParts[2])); } } @@ -146,8 +146,8 @@ private void ConstructFromFileCache() string[] parts = seqNumReader.ReadToEnd().Split(':'); if (parts.Length == 2) { - cache_.NextSenderMsgSeqNum = Convert.ToInt32(parts[0]); - cache_.NextTargetMsgSeqNum = Convert.ToInt32(parts[1]); + cache_.NextSenderMsgSeqNum = Convert.ToUInt64(parts[0]); + cache_.NextTargetMsgSeqNum = Convert.ToUInt64(parts[1]); } } } @@ -181,9 +181,9 @@ private void InitializeSessionCreateTime() /// /// /// - public void Get(int startSeqNum, int endSeqNum, List messages) + public void Get(SeqNumType startSeqNum, SeqNumType endSeqNum, List messages) { - for (int i = startSeqNum; i <= endSeqNum; i++) + for (SeqNumType i = startSeqNum; i <= endSeqNum; i++) { if (offsets_.ContainsKey(i)) { @@ -203,7 +203,7 @@ public void Get(int startSeqNum, int endSeqNum, List messages) /// /// /// - public bool Set(int msgSeqNum, string msg) + public bool Set(SeqNumType msgSeqNum, string msg) { msgFile_.Seek(0, System.IO.SeekOrigin.End); @@ -225,7 +225,7 @@ public bool Set(int msgSeqNum, string msg) return true; } - public int NextSenderMsgSeqNum { + public SeqNumType NextSenderMsgSeqNum { get { return cache_.NextSenderMsgSeqNum; } set { cache_.NextSenderMsgSeqNum = value; @@ -233,7 +233,7 @@ public int NextSenderMsgSeqNum { } } - public int NextTargetMsgSeqNum { + public SeqNumType NextTargetMsgSeqNum { get { return cache_.NextTargetMsgSeqNum; } set { cache_.NextTargetMsgSeqNum = value; @@ -258,7 +258,7 @@ private void setSeqNum() seqNumsFile_.Seek(0, System.IO.SeekOrigin.Begin); System.IO.StreamWriter writer = new System.IO.StreamWriter(seqNumsFile_); - writer.Write(NextSenderMsgSeqNum.ToString("D10") + " : " + NextTargetMsgSeqNum.ToString("D10") + " "); + writer.Write(NextSenderMsgSeqNum.ToString("D20") + " : " + NextTargetMsgSeqNum.ToString("D20") + " "); writer.Flush(); } diff --git a/QuickFIXn/GlobalUsing.cs b/QuickFIXn/GlobalUsing.cs new file mode 100644 index 000000000..7747ec155 --- /dev/null +++ b/QuickFIXn/GlobalUsing.cs @@ -0,0 +1,2 @@ +global using SeqNumType = System.UInt64; +global using SeqNumFieldType = QuickFix.Fields.ULongField; diff --git a/QuickFIXn/HttpServer.cs b/QuickFIXn/HttpServer.cs index 08916d88a..66161d173 100644 --- a/QuickFIXn/HttpServer.cs +++ b/QuickFIXn/HttpServer.cs @@ -381,15 +381,15 @@ public string SessionDetails(HttpListenerRequest request) if (request.QueryString["next incoming"] != null) { - int value = Convert.ToInt16(request.QueryString["next incoming"]); - sessionDetails.NextTargetMsgSeqNum = value <= 0 ? 1 : value; + SeqNumType val = Convert.ToUInt64(request.QueryString["next incoming"]); + sessionDetails.NextTargetMsgSeqNum = (val == 0 || val == SeqNumType.MaxValue) ? 1 : val; url = RemoveQueryStringByKey(urlOriginalString, "next incoming"); } if (request.QueryString["Next Outgoing"] != null) { - int value = Convert.ToInt16(request.QueryString["Next Outgoing"]); - sessionDetails.NextSenderMsgSeqNum = value <= 0 ? 1 : value; + SeqNumType val = Convert.ToUInt64(request.QueryString["Next Outgoing"]); + sessionDetails.NextSenderMsgSeqNum = (val == 0 || val == SeqNumType.MaxValue) ? 1 : val; url = RemoveQueryStringByKey(urlOriginalString, "Next Outgoing"); } @@ -413,22 +413,22 @@ public string SessionDetails(HttpListenerRequest request) if (request.QueryString["MaxLatency"] != null) { - int value = Convert.ToInt16(request.QueryString["MaxLatency"]); - sessionDetails.MaxLatency = value <= 0 ? 1 : value; + int val = Convert.ToInt16(request.QueryString["MaxLatency"]); + sessionDetails.MaxLatency = val <= 0 ? 1 : val; url = RemoveQueryStringByKey(urlOriginalString, "MaxLatency"); } if (request.QueryString["LogonTimeout"] != null) { - int value = Convert.ToInt16(request.QueryString["LogonTimeout"]); - sessionDetails.LogonTimeout = value <= 0 ? 1 : value; + int val = Convert.ToInt16(request.QueryString["LogonTimeout"]); + sessionDetails.LogonTimeout = val <= 0 ? 1 : val; url = RemoveQueryStringByKey(urlOriginalString, "LogonTimeout"); } if (request.QueryString["LogoutTimeout"] != null) { - int value = Convert.ToInt16(request.QueryString["LogoutTimeout"]); - sessionDetails.LogoutTimeout = value <= 0 ? 1 : value; + int val = Convert.ToInt16(request.QueryString["LogoutTimeout"]); + sessionDetails.LogoutTimeout = val <= 0 ? 1 : val; url = RemoveQueryStringByKey(urlOriginalString, "LogoutTimeout"); } @@ -520,30 +520,41 @@ public static string GetParameterList(string url) return HttpUtility.ParseQueryString((new Uri(url).Query)).ToString(); } - private static string AddRow(string colName, bool value, string url="") + private static string AddRow(string colName, bool val, string url="") { - string valueAsStr = value ? "yes" : "no"; + string valueAsStr = val ? "yes" : "no"; string innerHtml = url.Length > 0 - ? $"toggle" + ? $"toggle" : ""; return AddRow(colName, valueAsStr, innerHtml); } - private static string AddRow(string colName, int value, string url = "") + private static string AddRow(string colName, int val, string url = "") { - string innerHtml = $" << " + - $" < " + + string innerHtml = $" << " + + $" < " + " | " + - $" > " + - $" >> "; - return AddRow(colName, value.ToString(), innerHtml); + $" > " + + $" >> "; + return AddRow(colName, val.ToString(), innerHtml); } - private static string AddRow(string colName, string value, string innerHtml = "") + + private static string AddRow(string colName, SeqNumType val, string url = "") + { + string innerHtml = $" << " + + $" < " + + " | " + + $" > " + + $" >> "; + return AddRow(colName, val.ToString(), innerHtml); + } + + private static string AddRow(string colName, string val, string innerHtml = "") { StringBuilder row = new StringBuilder(); row.Append(""); row.Append(AddCell(colName)); - row.Append(AddCell(value)); + row.Append(AddCell(val)); row.Append(AddCell(innerHtml)); row.Append(""); return row.ToString(); diff --git a/QuickFIXn/IMessageStore.cs b/QuickFIXn/IMessageStore.cs index 035413e9f..a4657ccd9 100755 --- a/QuickFIXn/IMessageStore.cs +++ b/QuickFIXn/IMessageStore.cs @@ -16,7 +16,7 @@ public interface IMessageStore : IDisposable /// the starting message sequence number /// the ending message sequence number /// the retrieved messages (out parameter) - void Get(int startSeqNum, int endSeqNum, List messages); + void Get(SeqNumType startSeqNum, SeqNumType endSeqNum, List messages); /// /// Adds a raw fix message to the store with the give sequence number @@ -24,10 +24,10 @@ public interface IMessageStore : IDisposable /// the sequence number /// the raw FIX message string /// true if successful, false otherwise - bool Set(int msgSeqNum, string msg); + bool Set(SeqNumType msgSeqNum, string msg); - int NextSenderMsgSeqNum { get; set; } - int NextTargetMsgSeqNum { get; set; } + SeqNumType NextSenderMsgSeqNum { get; set; } + SeqNumType NextTargetMsgSeqNum { get; set; } void IncrNextSenderMsgSeqNum(); void IncrNextTargetMsgSeqNum(); diff --git a/QuickFIXn/MemoryStore.cs b/QuickFIXn/MemoryStore.cs index 97ba252c0..a32a25af3 100755 --- a/QuickFIXn/MemoryStore.cs +++ b/QuickFIXn/MemoryStore.cs @@ -10,20 +10,20 @@ public class MemoryStore : IMessageStore { #region Private Members - System.Collections.Generic.Dictionary messages_; + System.Collections.Generic.Dictionary messages_; DateTime? creationTime; #endregion public MemoryStore() { - messages_ = new System.Collections.Generic.Dictionary(); + messages_ = new System.Collections.Generic.Dictionary(); Reset(); } - public void Get(int begSeqNo, int endSeqNo, List messages) + public void Get(SeqNumType begSeqNo, SeqNumType endSeqNo, List messages) { - for (int current = begSeqNo; current <= endSeqNo; current++) + for (SeqNumType current = begSeqNo; current <= endSeqNo; current++) { if (messages_.ContainsKey(current)) messages.Add(messages_[current]); @@ -32,14 +32,14 @@ public void Get(int begSeqNo, int endSeqNo, List messages) #region MessageStore Members - public bool Set(int msgSeqNum, string msg) + public bool Set(SeqNumType msgSeqNum, string msg) { messages_[msgSeqNum] = msg; return true; } - public int NextSenderMsgSeqNum { get; set; } - public int NextTargetMsgSeqNum { get; set; } + public SeqNumType NextSenderMsgSeqNum { get; set; } + public SeqNumType NextTargetMsgSeqNum { get; set; } public void IncrNextSenderMsgSeqNum() { ++NextSenderMsgSeqNum; } diff --git a/QuickFIXn/Message/FieldMap.cs b/QuickFIXn/Message/FieldMap.cs index e12bea7e6..f44172fc0 100644 --- a/QuickFIXn/Message/FieldMap.cs +++ b/QuickFIXn/Message/FieldMap.cs @@ -167,6 +167,18 @@ public Fields.IntField GetField(Fields.IntField field) return field; } + /// + /// Gets a ulong field; saves its value into the parameter object, which is also the return value. + /// + /// this field's tag is used to extract the value from the message; that value is saved back into this object + /// thrown if isn't found + /// + public Fields.ULongField GetField(Fields.ULongField field) + { + field.Obj = GetULong(field.Tag); + return field; + } + /// /// Gets a decimal field; saves its value into the parameter object, which is also the return value. /// @@ -328,6 +340,28 @@ public int GetInt(int tag) } } + /// + /// Gets the ulong value of a field + /// + /// the FIX tag + /// the ulong field value + /// + public ulong GetULong(int tag) + { + try + { + Fields.IField fld = _fields[tag]; + if (fld.GetType() == typeof(ULongField)) + return ((ULongField)fld).Obj; + else + return ULongConverter.Convert(fld.ToString()); + } + catch (System.Collections.Generic.KeyNotFoundException) + { + throw new FieldNotFoundException(tag); + } + } + /// /// Gets the DateTime value of a field /// diff --git a/QuickFIXn/ResendRange.cs b/QuickFIXn/ResendRange.cs index 5ca7b0133..0ed685e1d 100755 --- a/QuickFIXn/ResendRange.cs +++ b/QuickFIXn/ResendRange.cs @@ -2,15 +2,17 @@ { public class ResendRange { - public int BeginSeqNo { get; set; } - public int EndSeqNo { get; set; } - public int ChunkEndSeqNo { get; set; } + public const SeqNumType NOT_SET = SeqNumType.MaxValue; + + public SeqNumType BeginSeqNo { get; set; } + public SeqNumType EndSeqNo { get; set; } + public SeqNumType ChunkEndSeqNo { get; set; } public ResendRange() { BeginSeqNo = 0; EndSeqNo = 0; - ChunkEndSeqNo = -1; + ChunkEndSeqNo = ResendRange.NOT_SET; } public override string ToString() diff --git a/QuickFIXn/Session.cs b/QuickFIXn/Session.cs index 3e9b8271b..128dae377 100755 --- a/QuickFIXn/Session.cs +++ b/QuickFIXn/Session.cs @@ -76,7 +76,7 @@ public bool IsNewSession /// /// Gets or sets the next expected outgoing sequence number /// - public int NextSenderMsgSeqNum + public SeqNumType NextSenderMsgSeqNum { get { @@ -91,7 +91,7 @@ public int NextSenderMsgSeqNum /// /// Gets or sets the next expected incoming sequence number /// - public int NextTargetMsgSeqNum + public SeqNumType NextTargetMsgSeqNum { get { @@ -210,7 +210,7 @@ public TimeStampPrecision TimeStampPrecision /// /// Sets a maximum number of messages to request in a resend request. /// - public int MaxMessagesInResendRequest { get; set; } + public SeqNumType MaxMessagesInResendRequest { get; set; } /// /// This is the FIX field value, e.g. "6" for FIX44 @@ -749,7 +749,7 @@ protected void NextLogon(Message logon) state_.SentReset = false; state_.ReceivedReset = false; - int msgSeqNum = logon.Header.GetInt(Fields.Tags.MsgSeqNum); + SeqNumType msgSeqNum = logon.Header.GetULong(Fields.Tags.MsgSeqNum); if (IsTargetTooHigh(msgSeqNum) && !resetSeqNumFlag.Obj) { DoTargetTooHigh(logon, msgSeqNum); @@ -777,11 +777,11 @@ protected void NextResendRequest(Message resendReq) return; try { - int msgSeqNum = 0; + SeqNumType msgSeqNum = 0; if (!(this.IgnorePossDupResendRequests && resendReq.Header.IsSetField(Tags.PossDupFlag))) { - int begSeqNo = resendReq.GetInt(Fields.Tags.BeginSeqNo); - int endSeqNo = resendReq.GetInt(Fields.Tags.EndSeqNo); + SeqNumType begSeqNo = resendReq.GetULong(Fields.Tags.BeginSeqNo); + SeqNumType endSeqNo = resendReq.GetULong(Fields.Tags.EndSeqNo); this.Log.OnEvent("Got resend request from " + begSeqNo + " to " + endSeqNo); if ((endSeqNo == 999999) || (endSeqNo == 0)) @@ -792,11 +792,11 @@ protected void NextResendRequest(Message resendReq) if (!PersistMessages) { endSeqNo++; - int next = state_.NextSenderMsgSeqNum; + SeqNumType next = state_.NextSenderMsgSeqNum; if (endSeqNo > next) endSeqNo = next; GenerateSequenceReset(resendReq, begSeqNo, endSeqNo); - msgSeqNum = resendReq.Header.GetInt(Tags.MsgSeqNum); + msgSeqNum = resendReq.Header.GetULong(Tags.MsgSeqNum); if (!IsTargetTooHigh(msgSeqNum) && !IsTargetTooLow(msgSeqNum)) { state_.IncrNextTargetMsgSeqNum(); @@ -806,13 +806,13 @@ protected void NextResendRequest(Message resendReq) List messages = new List(); state_.Get(begSeqNo, endSeqNo, messages); - int current = begSeqNo; - int begin = 0; + SeqNumType current = begSeqNo; + SeqNumType begin = 0; foreach (string msgStr in messages) { Message msg = new Message(); msg.FromString(msgStr, true, this.SessionDataDictionary, this.ApplicationDataDictionary, msgFactory_); - msgSeqNum = msg.Header.GetInt(Tags.MsgSeqNum); + msgSeqNum = msg.Header.GetULong(Tags.MsgSeqNum); if ((current != msgSeqNum) && begin == 0) { @@ -845,7 +845,7 @@ protected void NextResendRequest(Message resendReq) current = msgSeqNum + 1; } - int nextSeqNum = state_.NextSenderMsgSeqNum; + SeqNumType nextSeqNum = state_.NextSenderMsgSeqNum; if (++endSeqNo > nextSeqNum) { endSeqNo = nextSeqNum; @@ -861,7 +861,7 @@ protected void NextResendRequest(Message resendReq) GenerateSequenceReset(resendReq, begin, endSeqNo); } } - msgSeqNum = resendReq.Header.GetInt(Tags.MsgSeqNum); + msgSeqNum = resendReq.Header.GetULong(Tags.MsgSeqNum); if (!IsTargetTooHigh(msgSeqNum) && !IsTargetTooLow(msgSeqNum)) { state_.IncrNextTargetMsgSeqNum(); @@ -931,7 +931,7 @@ protected void NextSequenceReset(Message sequenceReset) if (sequenceReset.IsSetField(Fields.Tags.NewSeqNo)) { - int newSeqNo = sequenceReset.GetInt(Fields.Tags.NewSeqNo); + SeqNumType newSeqNo = sequenceReset.GetULong(Fields.Tags.NewSeqNo); this.Log.OnEvent("Received SequenceReset FROM: " + state_.NextTargetMsgSeqNum + " TO: " + newSeqNo); if (newSeqNo > state_.NextTargetMsgSeqNum) @@ -953,7 +953,7 @@ public bool Verify(Message message) public bool Verify(Message msg, bool checkTooHigh, bool checkTooLow) { - int msgSeqNum = 0; + SeqNumType msgSeqNum = 0; string msgType = ""; try @@ -970,7 +970,7 @@ public bool Verify(Message msg, bool checkTooHigh, bool checkTooLow) } if (checkTooHigh || checkTooLow) - msgSeqNum = msg.Header.GetInt(Fields.Tags.MsgSeqNum); + msgSeqNum = msg.Header.GetULong(Fields.Tags.MsgSeqNum); if (checkTooHigh && IsTargetTooHigh(msgSeqNum)) { @@ -994,7 +994,7 @@ public bool Verify(Message msg, bool checkTooHigh, bool checkTooLow) else if (msgSeqNum >= range.ChunkEndSeqNo) { this.Log.OnEvent("Chunked ResendRequest for messages FROM: " + range.BeginSeqNo + " TO: " + range.ChunkEndSeqNo + " has been satisfied."); - int newChunkEndSeqNo = Math.Min(range.EndSeqNo, range.ChunkEndSeqNo + this.MaxMessagesInResendRequest); + SeqNumType newChunkEndSeqNo = Math.Min(range.EndSeqNo, range.ChunkEndSeqNo + this.MaxMessagesInResendRequest); GenerateResendRequestRange(msg.Header.GetString(Fields.Tags.BeginString), range.ChunkEndSeqNo + 1, newChunkEndSeqNo); range.ChunkEndSeqNo = newChunkEndSeqNo; } @@ -1095,17 +1095,17 @@ protected bool IsTimeToGenerateLogon() return true; } - protected bool IsTargetTooHigh(int msgSeqNum) + protected bool IsTargetTooHigh(SeqNumType msgSeqNum) { return msgSeqNum > state_.NextTargetMsgSeqNum; } - protected bool IsTargetTooLow(int msgSeqNum) + protected bool IsTargetTooLow(SeqNumType msgSeqNum) { return msgSeqNum < state_.NextTargetMsgSeqNum; } - protected void DoTargetTooHigh(Message msg, int msgSeqNum) + protected void DoTargetTooHigh(Message msg, SeqNumType msgSeqNum) { string beginString = msg.Header.GetString(Fields.Tags.BeginString); @@ -1126,7 +1126,7 @@ protected void DoTargetTooHigh(Message msg, int msgSeqNum) GenerateResendRequest(beginString, msgSeqNum); } - protected void DoTargetTooLow(Message msg, int msgSeqNum) + protected void DoTargetTooLow(Message msg, SeqNumType msgSeqNum) { bool possDupFlag = false; if (msg.Header.IsSetField(Fields.Tags.PossDupFlag)) @@ -1176,7 +1176,7 @@ protected void DoPossDup(Message msg) protected void GenerateBusinessMessageReject(Message message, int err, int field) { string msgType = message.Header.GetString(Tags.MsgType); - int msgSeqNum = message.Header.GetInt(Tags.MsgSeqNum); + SeqNumType msgSeqNum = message.Header.GetULong(Tags.MsgSeqNum); string reason = FixValues.BusinessRejectReason.RejText[err]; Message reject; if (this.SessionID.BeginString.CompareTo(FixValues.BeginString.FIX42) >= 0) @@ -1202,7 +1202,7 @@ protected void GenerateBusinessMessageReject(Message message, int err, int field SendRaw(reject, 0); } - protected bool GenerateResendRequestRange(string beginString, int startSeqNum, int endSeqNum) + protected bool GenerateResendRequestRange(string beginString, SeqNumType startSeqNum, SeqNumType endSeqNum) { Message resendRequest = msgFactory_.Create(beginString, MsgType.RESEND_REQUEST); @@ -1222,11 +1222,11 @@ protected bool GenerateResendRequestRange(string beginString, int startSeqNum, i } } - protected bool GenerateResendRequest(string beginString, int msgSeqNum) + protected bool GenerateResendRequest(string beginString, SeqNumType msgSeqNum) { - int beginSeqNum = state_.NextTargetMsgSeqNum; - int endRangeSeqNum = msgSeqNum - 1; - int endChunkSeqNum; + SeqNumType beginSeqNum = state_.NextTargetMsgSeqNum; + SeqNumType endRangeSeqNum = msgSeqNum - 1; + SeqNumType endChunkSeqNum; if (this.MaxMessagesInResendRequest > 0) { endChunkSeqNum = Math.Min(endRangeSeqNum, beginSeqNum + this.MaxMessagesInResendRequest - 1); @@ -1288,7 +1288,7 @@ protected bool GenerateLogon(Message otherLogon) logon.SetField(new Fields.DefaultApplVerID(this.SenderDefaultApplVerID)); logon.SetField(new Fields.HeartBtInt(otherLogon.GetInt(Tags.HeartBtInt))); if (this.EnableLastMsgSeqNumProcessed) - logon.Header.SetField(new Fields.LastMsgSeqNumProcessed(otherLogon.Header.GetInt(Tags.MsgSeqNum))); + logon.Header.SetField(new Fields.LastMsgSeqNumProcessed(otherLogon.Header.GetULong(Tags.MsgSeqNum))); InitializeHeader(logon); state_.SentLogon = SendRaw(logon, 0); @@ -1348,7 +1348,7 @@ private bool GenerateLogout(Message other, string text) { try { - logout.Header.SetField(new Fields.LastMsgSeqNumProcessed(other.Header.GetInt(Tags.MsgSeqNum))); + logout.Header.SetField(new Fields.LastMsgSeqNumProcessed(other.Header.GetULong(Tags.MsgSeqNum))); } catch (FieldNotFoundException) { @@ -1375,7 +1375,7 @@ public bool GenerateHeartbeat(Message testRequest) heartbeat.SetField(new Fields.TestReqID(testRequest.GetString(Fields.Tags.TestReqID))); if (this.EnableLastMsgSeqNumProcessed) { - heartbeat.Header.SetField(new Fields.LastMsgSeqNumProcessed(testRequest.Header.GetInt(Tags.MsgSeqNum))); + heartbeat.Header.SetField(new Fields.LastMsgSeqNumProcessed(testRequest.Header.GetULong(Tags.MsgSeqNum))); } } catch (FieldNotFoundException) @@ -1413,12 +1413,12 @@ public bool GenerateReject(Message message, FixValues.SessionRejectReason reason else msgType = ""; - int msgSeqNum = 0; + SeqNumType msgSeqNum = 0; if (message.Header.IsSetField(Fields.Tags.MsgSeqNum)) { try { - msgSeqNum = message.Header.GetInt(Fields.Tags.MsgSeqNum); + msgSeqNum = message.Header.GetULong(Fields.Tags.MsgSeqNum); reject.SetField(new Fields.RefSeqNum(msgSeqNum)); } catch (System.Exception) @@ -1493,7 +1493,7 @@ protected void PopulateRejectReason(Message reject, string text) /// /// /// - protected void InitializeHeader(Message m, int msgSeqNum) + protected void InitializeHeader(Message m, SeqNumType msgSeqNum) { state_.LastSentTimeDT = DateTime.UtcNow; m.Header.SetField(new Fields.BeginString(this.SessionID.BeginString)); @@ -1540,7 +1540,7 @@ protected void Persist(Message message, string messageString) { if (this.PersistMessages) { - int msgSeqNum = message.Header.GetInt(Fields.Tags.MsgSeqNum); + SeqNumType msgSeqNum = message.Header.GetULong(Fields.Tags.MsgSeqNum); state_.Set(msgSeqNum, messageString); } state_.IncrNextSenderMsgSeqNum(); @@ -1560,12 +1560,12 @@ protected bool IsGoodTime(Message msg) return true; } - private void GenerateSequenceReset(Message receivedMessage, int beginSeqNo, int endSeqNo) + private void GenerateSequenceReset(Message receivedMessage, SeqNumType beginSeqNo, SeqNumType endSeqNo) { string beginString = this.SessionID.BeginString; Message sequenceReset = msgFactory_.Create(beginString, Fields.MsgType.SEQUENCE_RESET); InitializeHeader(sequenceReset); - int newSeqNo = endSeqNo; + SeqNumType newSeqNo = endSeqNo; sequenceReset.Header.SetField(new PossDupFlag(true)); InsertOrigSendingTime(sequenceReset.Header, sequenceReset.Header.GetDateTime(Tags.SendingTime)); @@ -1576,7 +1576,7 @@ private void GenerateSequenceReset(Message receivedMessage, int beginSeqNo, int { try { - sequenceReset.Header.SetField(new Fields.LastMsgSeqNumProcessed(receivedMessage.Header.GetInt(Tags.MsgSeqNum))); + sequenceReset.Header.SetField(new Fields.LastMsgSeqNumProcessed(receivedMessage.Header.GetULong(Tags.MsgSeqNum))); } catch (FieldNotFoundException) { @@ -1605,7 +1605,7 @@ protected void NextQueued() } } - protected bool NextQueued(int num) + protected bool NextQueued(SeqNumType num) { Message msg = state_.Dequeue(num); @@ -1633,7 +1633,7 @@ private bool IsAdminMessage(Message msg) return AdminMsgTypes.Contains(msgType); } - protected bool SendRaw(Message message, int seqNum) + protected bool SendRaw(Message message, SeqNumType seqNum) { lock (sync_) { diff --git a/QuickFIXn/SessionFactory.cs b/QuickFIXn/SessionFactory.cs index a714b4209..c5c66a509 100755 --- a/QuickFIXn/SessionFactory.cs +++ b/QuickFIXn/SessionFactory.cs @@ -146,7 +146,7 @@ public Session Create(SessionID sessionID, QuickFix.Dictionary settings) if (settings.Has(SessionSettings.ENABLE_LAST_MSG_SEQ_NUM_PROCESSED)) session.EnableLastMsgSeqNumProcessed = settings.GetBool(SessionSettings.ENABLE_LAST_MSG_SEQ_NUM_PROCESSED); if (settings.Has(SessionSettings.MAX_MESSAGES_IN_RESEND_REQUEST)) - session.MaxMessagesInResendRequest = settings.GetInt(SessionSettings.MAX_MESSAGES_IN_RESEND_REQUEST); + session.MaxMessagesInResendRequest = settings.GetULong(SessionSettings.MAX_MESSAGES_IN_RESEND_REQUEST); if (settings.Has(SessionSettings.SEND_LOGOUT_BEFORE_TIMEOUT_DISCONNECT)) session.SendLogoutBeforeTimeoutDisconnect = settings.GetBool(SessionSettings.SEND_LOGOUT_BEFORE_TIMEOUT_DISCONNECT); if (settings.Has(SessionSettings.IGNORE_POSSDUP_RESEND_REQUESTS)) diff --git a/QuickFIXn/SessionState.cs b/QuickFIXn/SessionState.cs index 0d1f0aadf..5563a4ada 100755 --- a/QuickFIXn/SessionState.cs +++ b/QuickFIXn/SessionState.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using MessagesBySeqNum = System.Collections.Generic.Dictionary; namespace QuickFix { @@ -29,7 +30,7 @@ public class SessionState : IDisposable private int logoutTimeout_ = 2; private long logoutTimeoutAsMilliSecs_ = 2 * 1000; private ResendRange resendRange_ = new ResendRange(); - private Dictionary msgQueue = new Dictionary(); + private MessagesBySeqNum msgQueue = new MessagesBySeqNum(); private ILog log_; @@ -147,7 +148,7 @@ public long LogoutTimeoutAsMilliSecs get { lock (sync_) { return logoutTimeoutAsMilliSecs_; } } } - private Dictionary MsgQueue + private MessagesBySeqNum MsgQueue { get { lock (sync_) { return msgQueue; } } set { lock (sync_) { msgQueue = value; } } @@ -273,7 +274,7 @@ public ResendRange GetResendRange() return resendRange_; } - public void Get(int begSeqNo, int endSeqNo, List messages) + public void Get(SeqNumType begSeqNo, SeqNumType endSeqNo, List messages) { lock (sync_) { @@ -281,16 +282,16 @@ public void Get(int begSeqNo, int endSeqNo, List messages) } } - public void SetResendRange(int begin, int end) + public void SetResendRange(SeqNumType begin, SeqNumType end) { - SetResendRange(begin, end, -1); + SetResendRange(begin, end, ResendRange.NOT_SET); } - public void SetResendRange(int begin, int end, int chunkEnd) + public void SetResendRange(SeqNumType begin, SeqNumType end, SeqNumType chunkEnd) { resendRange_.BeginSeqNo = begin; resendRange_.EndSeqNo = end; - resendRange_.ChunkEndSeqNo = chunkEnd == -1 ? end : chunkEnd; + resendRange_.ChunkEndSeqNo = chunkEnd == ResendRange.NOT_SET ? end : chunkEnd; } public bool ResendRequested() @@ -298,7 +299,7 @@ public bool ResendRequested() return !(resendRange_.BeginSeqNo == 0 && resendRange_.EndSeqNo == 0); } - public void Queue(int msgSeqNum, Message msg) + public void Queue(SeqNumType msgSeqNum, Message msg) { if (!MsgQueue.ContainsKey(msgSeqNum)) { @@ -311,7 +312,7 @@ public void ClearQueue() MsgQueue.Clear(); } - public QuickFix.Message Dequeue(int num) + public QuickFix.Message Dequeue(SeqNumType num) { if (MsgQueue.ContainsKey(num)) { @@ -322,7 +323,7 @@ public QuickFix.Message Dequeue(int num) return null; } - public Message Retrieve(int msgSeqNum) + public Message Retrieve(SeqNumType msgSeqNum) { Message msg = null; if (MsgQueue.ContainsKey(msgSeqNum)) @@ -355,18 +356,18 @@ public override string ToString() #region MessageStore-manipulating Members - public bool Set(int msgSeqNum, string msg) + public bool Set(SeqNumType msgSeqNum, string msg) { lock (sync_) { return this.MessageStore.Set(msgSeqNum, msg); } } - public int NextSenderMsgSeqNum + public SeqNumType NextSenderMsgSeqNum { get { lock (sync_) { return this.MessageStore.NextSenderMsgSeqNum; } } set { lock (sync_) { this.MessageStore.NextSenderMsgSeqNum = value; } } } - public int NextTargetMsgSeqNum + public SeqNumType NextTargetMsgSeqNum { get { lock (sync_) { return this.MessageStore.NextTargetMsgSeqNum; } } set { lock (sync_) { this.MessageStore.NextTargetMsgSeqNum = value; } } diff --git a/UnitTests/FileStoreTests.cs b/UnitTests/FileStoreTests.cs index cd3529d51..e6a8ea92a 100755 --- a/UnitTests/FileStoreTests.cs +++ b/UnitTests/FileStoreTests.cs @@ -113,6 +113,45 @@ public void IncNextTargetMsgSeqNumTest() Assert.AreEqual(2, _store.NextTargetMsgSeqNum); } + /// Using UInt64 seqnums per FIX Trading Community Continuous Markets Working Group recommendations. + [Test] + public void TestSeqNumLimitsForContinuousMarkets() + { + // Given the next seqnums are UInt64.MaxValue - 1 + _store.NextSenderMsgSeqNum = System.UInt64.MaxValue - 1; + _store.NextTargetMsgSeqNum = _store.NextSenderMsgSeqNum; + + // When the next seqnums are incremented + _store.IncrNextSenderMsgSeqNum(); + _store.IncrNextTargetMsgSeqNum(); + + // Then the next seqnums should be UInt64.MaxValue + Assert.AreEqual(System.UInt64.MaxValue, _store.NextSenderMsgSeqNum); + Assert.AreEqual(System.UInt64.MaxValue, _store.NextTargetMsgSeqNum); + + // When the store is reloaded from files + RebuildStore(); + + // Then the next seqnums should still be UInt64.MaxValue + Assert.AreEqual(System.UInt64.MaxValue, _store.NextSenderMsgSeqNum); + Assert.AreEqual(System.UInt64.MaxValue, _store.NextTargetMsgSeqNum); + + // When the next seqnums are incremented again + _store.IncrNextSenderMsgSeqNum(); + _store.IncrNextTargetMsgSeqNum(); + + // Then the next seqnums should overflow to zero + Assert.AreEqual(0, _store.NextSenderMsgSeqNum); + Assert.AreEqual(0, _store.NextTargetMsgSeqNum); + + // When the store is reloaded from files + RebuildStore(); + + // Then the next seqnums should still be zero + Assert.AreEqual(0, _store.NextSenderMsgSeqNum); + Assert.AreEqual(0, _store.NextTargetMsgSeqNum); + } + [Test] public void ResetTest() { diff --git a/UnitTests/GlobalUsing.cs b/UnitTests/GlobalUsing.cs new file mode 100644 index 000000000..7747ec155 --- /dev/null +++ b/UnitTests/GlobalUsing.cs @@ -0,0 +1,2 @@ +global using SeqNumType = System.UInt64; +global using SeqNumFieldType = QuickFix.Fields.ULongField; diff --git a/UnitTests/SessionStateTest.cs b/UnitTests/SessionStateTest.cs index 124d178c0..d16747b06 100755 --- a/UnitTests/SessionStateTest.cs +++ b/UnitTests/SessionStateTest.cs @@ -159,7 +159,7 @@ public void ThreadSafeSetAndGet() { Hashtable getTable = new Hashtable(1000);//only used in 1 thread at a time //Synchronously populate 1000 messages - for (int i = 1; i < 1000; i++) { + for (SeqNumType i = 1; i < 1000; i++) { string msg = "msg" + i; state.Set(i, msg); setTable[i] = msg; @@ -170,7 +170,7 @@ public void ThreadSafeSetAndGet() { ThreadPool.QueueUserWorkItem(delegate(object stateObject) { AutoResetEvent internalSetEvent = (AutoResetEvent)((object[])stateObject)[0]; SessionState internalState = (SessionState)((object[])stateObject)[1]; - for (int i = 1001; i < 2000; i++) { + for (SeqNumType i = 1001; i < 2000; i++) { try { internalState.Set(i, "msg" + i); } @@ -188,7 +188,7 @@ public void ThreadSafeSetAndGet() { ThreadPool.QueueUserWorkItem(delegate(object stateObject){ AutoResetEvent internalGetEvent = (AutoResetEvent)((object[])stateObject)[0]; SessionState internalState = (SessionState)((object[])stateObject)[1]; - for (int i = 1; i < 1000; i++) { + for (SeqNumType i = 1; i < 1000; i++) { try { List lst = new List(1); internalState.Get(i, i, lst); diff --git a/UnitTests/SessionTest.cs b/UnitTests/SessionTest.cs index ba0210dd6..ccfa316f4 100755 --- a/UnitTests/SessionTest.cs +++ b/UnitTests/SessionTest.cs @@ -175,7 +175,7 @@ public class SessionTest QuickFix.Session session = null; QuickFix.Session session2 = null; QuickFix.Dictionary config = null; - int seqNum = 1; + SeqNumType seqNum = 1; Regex msRegex = new Regex(@"\.[\d]{1,3}$"); Regex microsecondRegex = new Regex(@"\.[\d]{1,6}$"); @@ -348,14 +348,14 @@ public void SendNOSMessage() session.Next(order.ToString()); } - public void SendResendRequest(int begin, int end) + public void SendResendRequest(SeqNumType begin, SeqNumType end) { SendTheMessage(new QuickFix.FIX42.ResendRequest( new QuickFix.Fields.BeginSeqNo(begin), new QuickFix.Fields.EndSeqNo(end))); } - public void SendResendRequest40(int begin, int end) + public void SendResendRequest40(SeqNumType begin, SeqNumType end) { SendTheMessage(new QuickFix.FIX40.ResendRequest( new QuickFix.Fields.BeginSeqNo(begin), @@ -458,30 +458,30 @@ public void TestGapFillOnResend() order.Header.SetField(new QuickFix.Fields.TargetCompID(sessionID.TargetCompID)); order.Header.SetField(new QuickFix.Fields.SenderCompID(sessionID.SenderCompID)); - int[] gapStarts = new[] { 1, 5, 11 }; // 1st gap from seq num 1 to 2 is just the Logon message - int[] gapEnds = new[] { 2, 8, 15 }; + SeqNumType[] gapStarts = new[] { 1UL, 5UL, 11UL }; // 1st gap from seq num 1 to 2 is just the Logon message + SeqNumType[] gapEnds = new[] { 2UL, 8UL, 15UL }; int orderCount = 0; - for (int msgSeqNum = gapEnds[0]; msgSeqNum < gapStarts[1]; ++msgSeqNum) + for (SeqNumType msgSeqNum = gapEnds[0]; msgSeqNum < gapStarts[1]; ++msgSeqNum) { order.Header.SetField(new QuickFix.Fields.MsgSeqNum(msgSeqNum)); session.Send(order); ++orderCount; } //seq 4, next is 5 - for (int msgSeqNum = gapStarts[1]; msgSeqNum < gapEnds[1]; ++msgSeqNum) + for (SeqNumType msgSeqNum = gapStarts[1]; msgSeqNum < gapEnds[1]; ++msgSeqNum) { session.GenerateHeartbeat(); } //seq 7, next is 8 - for (int msgSeqNum = gapEnds[1]; msgSeqNum < gapStarts[2]; ++msgSeqNum) + for (SeqNumType msgSeqNum = gapEnds[1]; msgSeqNum < gapStarts[2]; ++msgSeqNum) { order.Header.SetField(new QuickFix.Fields.MsgSeqNum(msgSeqNum)); session.Send(order); ++orderCount; } //seq 10, next is 11 - for (int msgSeqNum = gapStarts[2]; msgSeqNum < gapEnds[2]; ++msgSeqNum) + for (SeqNumType msgSeqNum = gapStarts[2]; msgSeqNum < gapEnds[2]; ++msgSeqNum) { session.GenerateHeartbeat(); } // seq 11 - 14 @@ -496,7 +496,7 @@ public void TestGapFillOnResend() foreach (QuickFix.Message sequenceResestMsg in responder.msgLookup[QuickFix.Fields.MsgType.SEQUENCE_RESET]) { Assert.AreEqual(sequenceResestMsg.GetString(QuickFix.Fields.Tags.GapFillFlag), "Y"); - Assert.AreEqual(sequenceResestMsg.Header.GetInt(QuickFix.Fields.Tags.MsgSeqNum), gapStarts[++count]); + Assert.AreEqual(sequenceResestMsg.Header.GetULong(QuickFix.Fields.Tags.MsgSeqNum), gapStarts[++count]); Assert.AreEqual(sequenceResestMsg.GetInt(QuickFix.Fields.Tags.NewSeqNo), gapEnds[count]); } } @@ -654,7 +654,7 @@ public void TestLastMsgSeqNumProcessed() // Logon Logon(); QuickFix.Message msg = responder.msgLookup[QuickFix.Fields.MsgType.LOGON].Last(); - int lastSeqNumProcessed = msg.Header.GetInt(QuickFix.Fields.Tags.LastMsgSeqNumProcessed); + SeqNumType lastSeqNumProcessed = msg.Header.GetULong(QuickFix.Fields.Tags.LastMsgSeqNumProcessed); Assert.That(lastSeqNumProcessed == 1); // NOS @@ -672,7 +672,7 @@ public void TestLastMsgSeqNumProcessed() session.Send(order); msg = responder.msgLookup[QuickFix.Fields.MsgType.NEW_ORDER_D].Last(); - lastSeqNumProcessed = msg.Header.GetInt(QuickFix.Fields.Tags.LastMsgSeqNumProcessed); + lastSeqNumProcessed = msg.Header.GetULong(QuickFix.Fields.Tags.LastMsgSeqNumProcessed); Assert.That(lastSeqNumProcessed == 1); } diff --git a/generator/fields_gen.rb b/generator/fields_gen.rb index dcc315f99..f69781871 100644 --- a/generator/fields_gen.rb +++ b/generator/fields_gen.rb @@ -13,8 +13,10 @@ def self.type_info( field ) case field[:fldtype] when 'CHAR' {:cs_class => 'CharField', :base_type=>'char'} - when 'INT', 'NUMINGROUP', 'SEQNUM', 'LENGTH' + when 'INT', 'NUMINGROUP', 'LENGTH' {:cs_class => 'IntField', :base_type=>'int'} + when 'SEQNUM' + {:cs_class => 'ULongField', :base_type=>'ulong'} when 'AMT', 'PERCENTAGE', 'PRICE', 'QTY', 'PRICEOFFSET', 'FLOAT' {:cs_class => 'DecimalField', :base_type=>'Decimal'} when 'UTCTIMESTAMP', 'TZTIMESTAMP', 'TIME' diff --git a/generator/message_factory_gen.rb b/generator/message_factory_gen.rb index 55bb4b15b..3f69a6075 100644 --- a/generator/message_factory_gen.rb +++ b/generator/message_factory_gen.rb @@ -1,7 +1,7 @@ class MessageFactoryGen def self.generate(messages, dir, fixver) destdir = File.join(dir,fixver) - Dir.mkdir(destdir) unless File.exists?(destdir) + Dir.mkdir(destdir) unless File.exist?(destdir) file_path = File.join(destdir,"MessageFactory.cs") puts 'generate ' + file_path diff --git a/generator/messages_gen.rb b/generator/messages_gen.rb index da5c96020..2c03218ef 100644 --- a/generator/messages_gen.rb +++ b/generator/messages_gen.rb @@ -3,7 +3,7 @@ class MessageGen def self.generate messages, dir, fixver destdir = File.join(dir, fixver) - Dir.mkdir(destdir) unless File.exists? destdir + Dir.mkdir(destdir) unless File.exist? destdir basemsgstr = gen_basemsg(fixver,destdir) basemsg_path = File.join(destdir, "Message.cs") diff --git a/spec/fix/FIX40.xml b/spec/fix/FIX40.xml index 08bd0e7d9..455e91e89 100644 --- a/spec/fix/FIX40.xml +++ b/spec/fix/FIX40.xml @@ -463,7 +463,7 @@ - + @@ -476,7 +476,7 @@ - + @@ -548,9 +548,9 @@ - + - + @@ -592,7 +592,7 @@ - + diff --git a/spec/fix/FIX41.xml b/spec/fix/FIX41.xml index d1d470eb1..430fdfe9f 100644 --- a/spec/fix/FIX41.xml +++ b/spec/fix/FIX41.xml @@ -689,7 +689,7 @@ - + @@ -702,7 +702,7 @@ - + @@ -778,9 +778,9 @@ - + - + @@ -825,7 +825,7 @@ - + diff --git a/spec/fix/FIX42.xml b/spec/fix/FIX42.xml index d209e06d4..575fc6230 100644 --- a/spec/fix/FIX42.xml +++ b/spec/fix/FIX42.xml @@ -1615,7 +1615,7 @@ - + @@ -1628,7 +1628,7 @@ - + @@ -1708,9 +1708,9 @@ - + - + @@ -1758,7 +1758,7 @@ - + @@ -2491,7 +2491,7 @@ - +