/// <summary> /// The ParseHeader method attempts to parse the header of /// a FIX message from the buffer in the parser's context: /// </summary> /// <param name="ctx"> /// The parser's current context. /// </param> /// <param name="result"> /// The parser's result information. /// </param> private void ParseHeader(ParseContext ctx, VfxFixParserResult result) { // REC: Default to success, let the parse logic // override this if an error occurs: result.Status = VfxFixParserStatus.MsgComplete; // REC: Start extracting FIX fields from the buffer: while (ctx.Index < ctx.Buffer.Length) { // REC: Reset index value in case there // is an error parsing the buffer: int idxRst = ctx.Index; int idxSep = ctx.Buffer.IndexOf('=', ctx.Index); int idxSoh = ctx.Buffer.IndexOf('\x01', idxSep + 1); // REC: If the separator or SOH fields were not // found, then the header is incomplete and the // parsing cannot continue: if ((idxSep == -1) || (idxSoh == -1)) { result.Consumed = 0; result.Status = VfxFixParserStatus.MsgExhausted; return; } // REC: Extract the field's tag from the message: string strTag = ctx.Buffer.Substring(ctx.Index, idxSep - ctx.Index); // REC: Convert the field's tag from its string // form into its corresponding integer value: int nTag = -1; if (int.TryParse(strTag, out nTag) == false) { // REC: If the field cannot be converted into // an integer, the message is malformed: result.Consumed = 0; result.Status = VfxFixParserStatus.MsgMalformed; return; } // REC: If the FIX BeginString has already been parsed // from the buffer, and is encountered again, then the // message was incomplete and cannot be parsed: if (nTag == 8) { if (result.Message.Header.GetField(8) != null) { // REC: Reset the index into the message: ctx.Index = idxRst; // REC: Adjust the result accordingly: result.Consumed = 0; result.Status = VfxFixParserStatus.MsgIncomplete; return; } } // REC: Determine whether or not the version of the // session protocol has been discovered yet: if (ctx.HdrElements != null) { IFixDxElement hdrElement = ctx.HdrElements.GetElement(nTag); if (hdrElement != null) { if (hdrElement is FixDxResolvedField) { // REC: Retrieve the field's value: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); FixField field = new FixField(nTag, strVal); result.Message.Header.AddField(field); ctx.Index = idxSoh + 1; } else if (hdrElement is FixDxResolvedGroup) { FixDxResolvedGroup groupEntry = hdrElement as FixDxResolvedGroup; // REC: Since this field is a group entry it's ok // to assume that the value is the number of groups // that follow the group tag: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Convert the value into an integer and then // attempt to exract that number of repeating groups: int nGroups = 0; if (int.TryParse(strVal, out nGroups) == true) { // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; FixGroup group = new FixGroup(nTag, strVal); // REC: Try to parse N instances of the // repeating group from the message: ParseGroupResult parseResult = ParseGroup(ctx, groupEntry, nGroups); if (parseResult.Status != VfxFixParserStatus.MsgComplete) { return; } foreach (FixCollection instance in parseResult.Instances) { group.Instances.Add(instance); } result.Message.Header.AddGroup(group); } else { // REC: The value in the group tag couldn't // be converted into an integer, so the // number of repeating groups is unknown. FixGroup group = new FixGroup(nTag, strVal); result.Message.Header.AddGroup(group); // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; } } } else { // REC: The parser doesn't support UDFs in the // header section of the message, so the result // is considered complete if we hit a field that // isn't in the session protocol dictionary: break; } } else { // REC: The session protocol isn't known yet, so the // field is considered a plain FIX field and added to // the message's header: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); FixField field = new FixField(nTag, strVal); result.Message.Header.AddField(field); ctx.Index = idxSoh + 1; // REC: Attempt to determine the session layer protocol // based on the header elements that have been parsed: ctx.SxVersion = _vxMatcher.GetVersion(result.Message.Header, "session"); if (ctx.SxVersion == null) { // REC: If no session layer dictionary matched the // elements that have been parsed so far, check for // a combined version definition in case the session // is running FIX versions 4.0-4.4 which do not have // separate session and application layer versions: ctx.SxVersion = _vxMatcher.GetVersion(result.Message.Header, "combined"); } // REC: The elements in the message header correspond // to a specific data dictionary, so that should be // used for parsing the header and trailer elements: if (ctx.SxVersion != null) { InitCachedHdrElements(ctx.SxVersion); InitCachedTrlElements(ctx.SxVersion); // REC: Assign the cached sets of resolved header // and trailer elements to the parse context: ctx.HdrElements = _hdrElements[ctx.SxVersion]; ctx.TrlElements = _trlElements[ctx.SxVersion]; } } } // REC: Determine if the message belongs to the session // layer protocol as opposed to the application layer: if (ctx.SxVersion != null) { // REC: Retrieve the FIX message type: FixField msgType = result.Message.Header.GetField(35); if (!string.IsNullOrEmpty(msgType.Content)) { // REC: Check the version definition registry for the // corresponding protocol version definition: VfxFixVxRecord vxDefinition = this._vxRegistry.Get(ctx.SxVersion); if (vxDefinition != null) { // REC: Retrieve the associated dictionary from the // configured FIX dictionary registry: FixDictionary dxInstance = this._dxRegistry.GetEntry(vxDefinition.Dictionaries[0].Name); if (dxInstance != null) { // REC: Check if the message is defined within the // dictionary for the session layer: FixDxMessage sxMessage = dxInstance.GetMessageByType(msgType.Content); if (sxMessage != null) { if ((!_msgElements.ContainsKey(ctx.SxVersion)) || (!_msgElements[ctx.SxVersion].Elements.ContainsKey(msgType.Content))) { InitCachedMsgElements(ctx, msgType.Content); } // REC: Look in the message definition cache // for this particular message type: if (_msgElements[ctx.SxVersion].Elements.ContainsKey(msgType.Content)) { ctx.MsgElements = _msgElements[ctx.SxVersion].Elements[msgType.Content]; } // REC: The context is now initialized to parse the // session layer message's content, so we're done: return; } } } } } // REC: If an override version has been specified for the // application layer, use that instead of hunting for it: if (ctx.AxVersion == null) { // REC: Now that the entire header of the message has been // extracted from the buffer, test the header contents and // determine the application protocol version: ctx.AxVersion = _vxMatcher.GetVersion(result.Message.Header, "application"); if (ctx.AxVersion == null) { // REC: If the application layer version cannot be // determined, it may be a FIX 4.0-4.4 message, so // check the combined versions as well: ctx.AxVersion = _vxMatcher.GetVersion(result.Message.Header, "combined"); if(ctx.AxVersion == null) { // REC: If the application layer could not be // resolved, then switch to the default: ctx.AxVersion = ctx.AxDefault; } } } if (ctx.AxVersion != null) { // REC: Now that the application version has been // determined, retrieve the message elements for // the type of message being parsed, initializing // the local message elements cache if needed: FixField msgType = result.Message.Header.GetField(35); if (msgType != null) { if ((!_msgElements.ContainsKey(ctx.AxVersion)) || (!_msgElements[ctx.AxVersion].Elements.ContainsKey(msgType.Content))) { InitCachedMsgElements(ctx, msgType.Content); } // REC: Look in the message definition cache // for this particular message type: if (_msgElements[ctx.AxVersion].Elements.ContainsKey(msgType.Content)) { ctx.MsgElements = _msgElements[ctx.AxVersion].Elements[msgType.Content]; } } } }
/// <summary> /// The Parse method attempts to extract a single instance /// of a FIX message from a supplied string. /// </summary> /// <param name="buffer"> /// The string representation of the buffer that the parser /// is to extract the message instance from. /// </param> /// <param name="sxVersion"> /// The name of the FIX session layer version definition that /// is to be used. If null, then the parser will determine the /// relevant session layer version by executing a pattern match /// against all of the configured version definitions. /// </param> /// <param name="axVersion"> /// The name of the FIX application layer version definition the /// parser will use to parse the content of the message. If this /// parameter is null, then the parser will attempt to determine /// the application layer version by executing a pattern match /// against all of the configured version definitions. /// </param> /// <param name="axDefault"> /// The name of the FIX application layer version definition the /// parser will revert to, if it cannot determine the version by /// executing a pattern match against the version definitions. /// </param> /// <returns> /// An instance of VfxFixParserResult that contains the /// result information for the parse attempt. /// </returns> public VfxFixParserResult Parse(string buffer, string sxVersion, string axVersion, string axDefault) { VfxFixParserResult result = new VfxFixParserResult(); // REC: Don't bother if there is no data // in the supplied buffer: if (buffer.Length == 0) { result.Status = VfxFixParserStatus.MsgExhausted; return result; } // REC: Construct the new context that will be used // to track the parser's state: ParseContext context = new ParseContext(buffer, 0); // REC: If an override has been specified for either the // session or application layer versions, assign it to the // context instance: if (!string.IsNullOrEmpty(sxVersion)) { context.SxVersion = sxVersion; } if (!string.IsNullOrEmpty(axVersion)) { context.AxVersion = axVersion; } if (!string.IsNullOrEmpty(axDefault)) { context.AxDefault = axDefault; } // REC: Attempt to parse the header from the message: ParseHeader(context, result); // REC: Attempt to parse the content from the message: ParseContent(context, result); // REC: Attempt to parse the trailer form the message: ParseTrailer(context, result); // REC: If a complete message was extracted from the // buffer, the number of characters that were consumed // to parse the message will be provided in the result // information. If no message was parsed, the consumed // count remains at zero so that the caller knows none // of the provided data was used by the parser. if (result.Status == VfxFixParserStatus.MsgComplete) { result.Consumed = context.Index; } return result; }
/// <summary> /// The ParseContent method attempts to parse the message body /// from an instance of a FIX message. /// </summary> /// <param name="ctx"> /// The parser's context information. /// </param> /// <param name="result"> /// The parser's result information. /// </param> private void ParseContent(ParseContext ctx, VfxFixParserResult result) { // REC: Attempt to retrieve the FIX message type // from the header of the message: FixField msgType = result.Message.Header.GetField(35); while (ctx.Index < ctx.Buffer.Length) { int idxSep = ctx.Buffer.IndexOf('=', ctx.Index); int idxSoh = ctx.Buffer.IndexOf('\x01', idxSep + 1); // REC: If the separator or SOH fields were not // found, then the message is incomplete and the // parsing cannot continue: if ((idxSep == -1) || (idxSoh == -1)) { result.Status = VfxFixParserStatus.MsgExhausted; return; } // REC: Extract the field's tag from the message: string strTag = ctx.Buffer.Substring(ctx.Index, idxSep - ctx.Index); // REC: Convert the field's tag to an integer: int nTag; if (int.TryParse(strTag, out nTag) == false) { result.Status = VfxFixParserStatus.MsgMalformed; return; } // REC: Test for premature message termination: if (nTag == 8) { result.Status = VfxFixParserStatus.MsgMalformed; return; } if (ctx.MsgElements != null) { IFixDxElement element = ctx.MsgElements.GetElement(nTag); if (element != null) { if (element is FixDxResolvedField) { // REC: Retrieve the field's value: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Create a new field to represent // the parsed field/value pair: FixField field = new FixField(nTag, strVal); result.Message.AddField(field); ctx.Index = idxSoh + 1; } else if (element is FixDxResolvedGroup) { // REC: Since this field is a group entry it's ok // to assume that the value is the number of groups // that follow the group tag: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Convert the value into an integer and then // attempt to exract that number of repeating groups: int nGroups = 0; if (int.TryParse(strVal, out nGroups) == true) { // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; FixGroup group = new FixGroup(nTag, strVal); // REC: Try to parse N instances of the // repeating group from the message: FixDxResolvedGroup resolvedGroup = element as FixDxResolvedGroup; ParseGroupResult parseResult = ParseGroup(ctx, resolvedGroup, nGroups); if (parseResult.Status != VfxFixParserStatus.MsgComplete) { result.Status = parseResult.Status; return; } foreach (FixCollection instance in parseResult.Instances) { group.Instances.Add(instance); } result.Message.AddGroup(group); } else { // REC: The value in the group tag couldn't // be converted into an integer, so the // number of repeating groups is unknown. FixGroup group = new FixGroup(nTag, strVal); result.Message.AddGroup(group); // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; } } } else { // REC: The tag wasn't found in the map of elements // that are known for this message type, so determine // whether it is a UDF or a field from the trailer: if (ctx.TrlElements.GetElement(nTag) != null) { // REC: The field is a trailer element // so content parsing is now complete: return; } else { // REC: The field is just a UDF so it can be added // to the message body: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); FixField field = new FixField(nTag, strVal); result.Message.AddField(field); ctx.Index = idxSoh + 1; } } } else { // REC: There is no cached set of elements for this // type of message, so the field is either a trailer // field or it is a user-defined field: if (ctx.TrlElements.GetElement(nTag) != null) { // REC: The field is a trailer element // so content parsing is now complete: return; } else { // REC: The field is just a UDF so it can be added // to the message body: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); FixField field = new FixField(nTag, strVal); result.Message.AddField(field); ctx.Index = idxSoh + 1; } } } }
private void ParseTrailer(ParseContext ctx, VfxFixParserResult result) { while (ctx.Index < ctx.Buffer.Length) { int idxSep = ctx.Buffer.IndexOf('=', ctx.Index); int idxSoh = ctx.Buffer.IndexOf('\x01', idxSep + 1); // REC: If the separator or SOH fields were not // found, then the message is incomplete and the // parsing cannot continue: if ((idxSep == -1) || (idxSoh == -1)) { // result.Consumed = 0; result.Status = VfxFixParserStatus.MsgExhausted; return; } // REC: Extract the field's tag from the message: string strTag = ctx.Buffer.Substring(ctx.Index, idxSep - ctx.Index); // REC: Convert the field's tag to an integer: int nTag; if (int.TryParse(strTag, out nTag) == false) { result.Status = VfxFixParserStatus.MsgMalformed; return; } if (ctx.TrlElements != null) { IFixDxElement element = ctx.TrlElements.GetElement(nTag); if(element != null) { if (element is FixDxResolvedField) { // REC: Retrieve the field's value: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Create a new field to represent // the parsed field/value pair: FixField field = new FixField(nTag, strVal); result.Message.Trailer.AddField(field); ctx.Index = idxSoh + 1; } else if (element is FixDxResolvedGroup) { FixDxResolvedGroup groupEntry = element as FixDxResolvedGroup; // REC: Since this field is a group entry it's ok // to assume that the value is the number of groups // that follow the group tag: string strVal = ctx.Buffer.Substring(idxSep + 1, idxSoh - (idxSep + 1)); // REC: Convert the value into an integer and then // attempt to exract that number of repeating groups: int nGroups = 0; if (int.TryParse(strVal, out nGroups) == true) { // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; FixGroup group = new FixGroup(nTag, strVal); // REC: Try to parse N instances of the // repeating group from the message: ParseGroupResult parseResult = ParseGroup(ctx, groupEntry, nGroups); if (parseResult.Status != VfxFixParserStatus.MsgComplete) { result.Status = parseResult.Status; return; } foreach (FixCollection instance in parseResult.Instances) { group.Instances.Add(instance); } result.Message.Trailer.AddGroup(group); } else { // REC: The value in the group tag couldn't // be converted into an integer, so the // number of repeating groups is unknown. FixGroup group = new FixGroup(nTag, strVal); result.Message.Trailer.AddGroup(group); // REC: Move the context's read index ahead // to compensate for reading the repeating // group's tag and associated value: ctx.Index = idxSoh + 1; } } } else { // REC: If the element is not in the set of trailer // elements, then the parsing is complete. return; } } else { result.Status = VfxFixParserStatus.MsgUnkSxProtocol; return; } } }