FixAntenna/NetCore/Message/RawFixUtil.cs (786 lines of code) (raw):
// Copyright (c) 2021 EPAM Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Epam.FixAntenna.Constants.Fixt11;
using Epam.FixAntenna.NetCore.Common;
using Epam.FixAntenna.NetCore.Common.Logging;
using Epam.FixAntenna.NetCore.Common.Pool;
using Epam.FixAntenna.NetCore.Common.Pool.Provider;
using Epam.FixAntenna.NetCore.Configuration;
using Epam.FixAntenna.NetCore.Helpers;
using Epam.FixAntenna.NetCore.Message.Rg;
using Epam.FixAntenna.NetCore.Message.Rg.Exceptions;
namespace Epam.FixAntenna.NetCore.Message
{
using TIntHashSet = HashSet<int>;
public sealed class RawFixUtil
{
private static readonly ILog Log = LogFactory.GetLog(typeof(RawFixUtil));
internal static readonly IRawTags DefaultRawTags = new DefaultRawTags();
private static readonly byte[] EmptyValue = Array.Empty<byte>();
private static readonly byte[] LogonBytes = "A".AsByteArray();
internal static IPool<TagValue> FieldPool;
internal static IPool<FixMessage> FieldListPool;
static RawFixUtil()
{
{
FieldPool = PoolFactory.GetConcurrentBucketsPool(10, 200, 2000,
new TagValueProvider());
FieldListPool = PoolFactory.GetConcurrentBucketsPool(10, 200, 2000,
new FixMessageProvider());
}
}
private RawFixUtil()
{
}
/// <summary>
/// Calculates checksum in array of bytes.
/// </summary>
/// <param name="bytes"> array of bytes </param>
public static int GetChecksum(byte[] bytes)
{
return GetChecksum(bytes, 0, bytes.Length);
}
/// <summary>
/// Calculates checksum in array of bytes.
/// </summary>
/// <param name="bytes"> the array of bytes </param>
/// <param name="offset"> the offset in array of bytes </param>
/// <param name="length"> the length of bytes </param>
public static int GetChecksum(byte[] bytes, int offset, int length)
{
var checksum = 0;
if (offset + length > bytes.Length)
{
throw new ArgumentException(
"Argument error. Byte array length is less then offset + length. Bytearray length: " +
bytes.Length + " Offset: " + offset + " Length: " + length);
}
for (var i = offset; i < offset + length; i++)
{
checksum += (char)bytes[i];
}
return checksum % 256;
}
/// <summary>
/// Calculates checksum in matrix of bytes.
/// </summary>
/// <param name="bytes"> the array of bytes </param>
/// <param name="offset"> the offset in array of bytes </param>
/// <param name="length"> the length of bytes </param>
internal static int GetChecksum(byte[][] bytes, int offset, int length)
{
var checksum = 0;
var wholeLength = 0;
foreach (var item in bytes)
{
wholeLength += item.Length;
}
var off = 0;
if (offset + length > wholeLength)
{
throw new ArgumentException(
"Argument error. Byte array length is less then offset + length. Byte matrix length: " +
wholeLength + " Offset: " + offset + " Length: " + length);
}
foreach (var item in bytes)
{
foreach (var nested in item)
{
off++;
if (off > offset && off <= offset + length)
{
checksum += (char)nested;
}
}
}
return checksum % 256;
}
/// <summary>
/// Gets the value from buffer as bytes.
/// </summary>
/// <param name="buffer"> the buffer of bytes </param>
/// <param name="offset"> the offset in buffer </param>
/// <param name="length"> the buffer length </param>
/// <param name="tag"> the tag id </param>
/// <param name="searchFromStart"> search from start or end of the given buffer </param>
public static byte[] GetRawValue(byte[] buffer, int offset, int length, int tag, bool searchFromStart = true)
{
int seqStart;
if (searchFromStart)
{
seqStart = GetStartValueFromStartBuffer(buffer, offset, length, tag);
}
else
{
seqStart = GetStartValueFromEndBuffer(buffer, offset, length, tag);
}
// if tag is not exist, return null
if (seqStart == -1)
{
return null;
}
var j = 0;
// count the length of tag value
while (seqStart + j < buffer.Length && buffer[seqStart + j] != 1)
{
j++;
}
var result = new byte[j];
Array.Copy(buffer, seqStart, result, 0, j);
return result;
}
/// <summary>
/// Gets the value from buffer as bytes.
/// </summary>
/// <param name="buffer"> the buffer of bytes </param>
/// <param name="offset"> the offset in buffer </param>
/// <param name="length"> the buffer length </param>
/// <param name="tag"> the tag id </param>
internal static IList<byte[]> GetAllRawValues(byte[] buffer, int offset, int length, int tag)
{
IList<byte[]> result = new List<byte[]>();
var relativePosition = 0;
int seqStart;
while (relativePosition < length &&
(seqStart = GetStartValueFromStartBuffer(buffer, offset + relativePosition,
length - relativePosition, tag)) != -1)
{
var j = 0;
// count the length of tag value
while (seqStart + j < buffer.Length && buffer[seqStart + j] != 1)
{
j++;
}
var tagValue = new byte[j];
Array.Copy(buffer, seqStart, tagValue, 0, j);
result.Add(tagValue);
relativePosition = seqStart + j - offset;
}
return result;
}
internal static int GetStartValueFromEndBuffer(byte[] buffer, int offset, int length, int tagId)
{
var tagIdLength = FixTypes.FormatIntLength(tagId);
var startIndex = -1;
for (var i = length - tagIdLength; i >= offset; i--)
{
if (buffer[i] == 1)
{
// is SOH
if (i + tagIdLength + 1 >= buffer.Length || buffer[i + tagIdLength + 1] != (byte)'=')
{
goto outerContinue; // next iteration
}
var tempTagId = tagId;
var pos = i + tagIdLength + 1;
do
{
if (buffer[--pos] != tempTagId % 10 + '0')
{
goto outerContinue; // next iteration
}
} while ((tempTagId /= 10) > 0);
startIndex = i + tagIdLength + 2;
break;
}
outerContinue: ;
}
return startIndex;
}
internal static int GetStartValueFromStartBuffer(byte[] buffer, int offset, int length, int tagId)
{
var tagIdLength = FixTypes.FormatIntLength(tagId);
var lookUpToPosition = offset + length - tagIdLength;
var startIndex = -1;
for (var i = offset; i < lookUpToPosition; i++)
{
if (i == offset || buffer[i - 1] == 1)
{
// is SOH
if (buffer[i + tagIdLength] != (byte)'=')
{
goto outerContinue; // next iteration
}
var tempTagId = tagId;
var pos = i + tagIdLength;
do
{
if (buffer[--pos] != tempTagId % 10 + '0')
{
goto outerContinue; // next iteration
}
} while ((tempTagId /= 10) > 0);
startIndex = i + tagIdLength + 1;
break;
}
outerContinue: ;
}
return startIndex;
}
/// <summary>
/// Gets the value from buffer as long.
/// </summary>
/// <param name="buffer"> the buffer of bytes </param>
/// <param name="offset"> the offset in buffer </param>
/// <param name="length"> the buffer length </param>
/// <param name="tag"> the tag id </param>
/// <exception cref="ArgumentException"> if tag not exists </exception>
internal static long GetLongValue(byte[] buffer, int offset, int length, int tag)
{
return GetLongValue(buffer, offset, length, tag, true);
}
/// <summary>
/// Gets the value from buffer as long.
/// </summary>
/// <param name="buffer"> the buffer of bytes </param>
/// <param name="offset"> the offset in buffer </param>
/// <param name="length"> the buffer length </param>
/// <param name="tag"> the tag id </param>
/// <param name="searchFromStart"> search from the start or end buffer </param>
/// <exception cref="ArgumentException"> if tag not exists </exception>
internal static long GetLongValue(byte[] buffer, int offset, int length, int tag, bool searchFromStart)
{
int startIndexValue;
if (searchFromStart)
{
startIndexValue = GetStartValueFromStartBuffer(buffer, offset, length, tag);
}
else
{
startIndexValue = GetStartValueFromEndBuffer(buffer, offset, length, tag);
}
if (startIndexValue == -1)
{
throw new ArgumentException("Tag " + tag + " is missing or has invalid value");
}
var lengthValue = 0;
// count the length of tag value
while (startIndexValue + lengthValue < buffer.Length && buffer[startIndexValue + lengthValue] != 1)
{
lengthValue++;
}
return FixTypes.ParseInt(buffer, startIndexValue, lengthValue);
}
/// <summary>
/// Gets sequence number.
/// </summary>
/// <param name="message"> the buffer of bytes </param>
/// <exception cref="ArgumentException"> if tag not exists </exception>
internal static long GetSequenceNumber(byte[] message)
{
return GetLongValue(message, 0, message.Length, Tags.MsgSeqNum);
}
/// <summary>
/// Gets sequence number.
/// </summary>
/// <param name="message"> the buffer of bytes </param>
/// <param name="offset"> the offset in buffer </param>
/// <param name="length"> the buffer length </param>
/// <exception cref="ArgumentException"> if tag not exists </exception>
public static long GetSequenceNumber(byte[] message, int offset, int length)
{
return GetLongValue(message, offset, length, Tags.MsgSeqNum);
}
public static FixMessage GetFixMessage(byte[] buffer, int messageOffset, int messageLength)
{
return GetFixMessage(buffer, messageOffset, messageLength, DefaultRawTags);
}
public static FixMessage GetFixMessage(MsgBuf buf)
{
return GetFixMessage(buf.Buffer, buf.Offset, buf.Length, DefaultRawTags);
}
internal static FixMessage GetFixMessage(MsgBuf buf, IRawTags rawTags)
{
return GetFixMessage(buf.Buffer, buf.Offset, buf.Length, rawTags, false, false);
}
internal static FixMessage GetFixMessage(FixMessage target, MsgBuf buf, IRawTags rawTags,
bool clearTheMessageBeforeUse)
{
return GetFixMessage(target, buf.Buffer, buf.Offset, buf.Length, rawTags, clearTheMessageBeforeUse);
}
/// <summary>
/// Creates index by dictionary for repeating group.
/// After calling this method, its possible to use Repeating Group API
/// Version and type of message will be got from message
/// Validation is turn off </summary>
/// <param name="msg"> message for indexing </param>
/// <returns> the passed message </returns>
public static FixMessage IndexRepeatingGroup(FixMessage msg)
{
var version = msg.GetFixVersion();
var msgType = GetMsgType(msg);
return IndexRepeatingGroup(msg, version, msgType, false);
}
public static string GetMsgType(IndexedStorage msg)
{
var index = msg.GetTagIndex(Tags.MsgType);
if (index == IndexedStorage.NotFound)
{
return null;
}
return Encoding.UTF8.GetString(msg.GetTagValueAsBytesAtIndex(index));
}
/// <summary>
/// Creates index by dictionary for repeating group.
/// After calling this method, its possible to use Repeating Group API
/// Version and type of message will be got from message </summary>
/// <param name="msg"> message for indexing </param>
/// <param name="validation"> turn on/off validation </param>
/// <returns> the passed message </returns>
public static FixMessage IndexRepeatingGroup(FixMessage msg, bool validation)
{
return (FixMessage)IndexRepeatingGroup((IndexedStorage)msg, validation);
}
public static IndexedStorage IndexRepeatingGroup(IndexedStorage msg, bool validation)
{
var version = msg.GetFixVersion();
if (version == null)
{
throw new ArgumentException("There is no info about FIX version in message. Please use method " +
"indexRepeatingGroup(FixMessage, FixVersion, MessageType");
}
var msgType = GetMsgType(msg);
if (msgType == null)
{
throw new ArgumentException("There is no info about message type in message. Please use method " +
"indexRepeatingGroup(FixMessage, FixVersion, MessageType");
}
return IndexRepeatingGroup(msg, version, msgType, validation);
}
/// <summary>
/// Creates index by dictionary for repeating group.
/// After calling this method, its possible to use Repeating Group API
/// Validation is turn off </summary>
/// <param name="msg"> message for indexing </param>
/// <param name="version"> version for passed message. Used for choose dictionary for indexing message </param>
/// <param name="msgType"> type of passed message. Used for indexing message. </param>
/// <returns> the passed message </returns>
public static FixMessage IndexRepeatingGroup(FixMessage msg, FixVersion version, string msgType)
{
return IndexRepeatingGroup(msg, FixVersionContainer.GetFixVersionContainer(version), msgType);
}
public static FixMessage IndexRepeatingGroup(FixMessage msg, FixVersionContainer version, string msgType)
{
return IndexRepeatingGroup(msg, version, msgType, false);
}
/// <summary>
/// Creates index by dictionary for repeating group.
/// After calling this method, its possible to use Repeating Group API </summary>
/// <param name="msg"> message for indexing </param>
/// <param name="version"> version for passed message. Used for choose dictionary for indexing message </param>
/// <param name="msgType"> type of passed message. Used for indexing message. </param>
/// <param name="validation"> turn on/off validation </param>
/// <returns> the passed message </returns>
public static FixMessage IndexRepeatingGroup(FixMessage msg, FixVersion version, string msgType,
bool validation)
{
return IndexRepeatingGroup(msg, FixVersionContainer.GetFixVersionContainer(version), msgType, validation);
}
public static FixMessage IndexRepeatingGroup(FixMessage msg, FixVersionContainer version, string msgType,
bool validation)
{
return (FixMessage)IndexRepeatingGroup((IndexedStorage)msg, version, msgType, validation);
}
public static FixMessage IndexRepeatingGroup(FixMessage msg, FixVersion version, bool validation)
{
return IndexRepeatingGroup(msg, FixVersionContainer.GetFixVersionContainer(version), validation);
}
public static FixMessage IndexRepeatingGroup(FixMessage msg, FixVersionContainer version, bool validation)
{
var msgType = GetMsgType(msg);
if (version == null || msgType == null)
{
throw new ArgumentException(
"There is no info about message type in message. Please use method indexRepeatingGroup(FixMessage, FixVersion, MessageType");
}
return (FixMessage)IndexRepeatingGroup((IndexedStorage)msg, version, msgType, validation);
}
public static IndexedStorage IndexRepeatingGroup(IndexedStorage msg, FixVersion version, string msgType,
bool validation)
{
return IndexRepeatingGroup(msg, FixVersionContainer.GetFixVersionContainer(version), msgType, validation);
}
public static IndexedStorage IndexRepeatingGroup(IndexedStorage msg, FixVersionContainer version,
string msgType, bool validation)
{
msg.InitRepeatingGroupStorage(version, msgType, validation);
if (validation)
{
return IndexRepeatingGroupWithValidation(msg, version, msgType);
}
return IndexRepeatingGroupWithoutValidation(msg, version, msgType);
}
private static IndexedStorage IndexRepeatingGroupWithValidation(IndexedStorage msg, FixVersionContainer version,
string msgType)
{
var dict = DictionaryHolder.GetDictionary(version);
var messageWithGroupDict = dict.GetMessageDict(msgType);
if (messageWithGroupDict != null)
{
var leadingTags = messageWithGroupDict.GetOuterLeadingTags();
var nestedTags = messageWithGroupDict.GetNestedLeadingTagsArray();
var allGroupTags = messageWithGroupDict.GetAllGroupTags();
leadingTags.UnionWith(nestedTags);
for (var i = 0; i < msg.Count; i++)
{
var tag = msg.GetTagIdAtIndex(i);
if (leadingTags.Contains(tag) && msg.GetTagValueAsLongAtIndex(i) > 0)
{
i = ParseGroup(msg, messageWithGroupDict, i, tag, leadingTags, 0, version, msgType, true) - 1;
}
else if (allGroupTags.Contains(tag))
{
throw new UnexpectedGroupTagException(tag, version, msgType);
}
}
}
return msg;
}
private static IndexedStorage IndexRepeatingGroupWithoutValidation(IndexedStorage msg,
FixVersionContainer version, string msgType)
{
var dict = DictionaryHolder.GetDictionary(version);
var messageWithGroupDict = dict.GetMessageDict(msgType);
if (messageWithGroupDict != null)
{
var outerLeadingTags = messageWithGroupDict.GetOuterLeadingTagsArray();
var nestedTags = messageWithGroupDict.GetNestedLeadingTags();
foreach (var leadingTag in outerLeadingTags)
{
if (msg.IsTagExists(leadingTag) && msg.GetTagValueAsLongAtIndex(msg.GetTagIndex(leadingTag)) > 0)
{
var parseStartIndex = msg.GetTagIndex(leadingTag);
ParseGroup(msg, messageWithGroupDict, parseStartIndex, leadingTag, nestedTags, 0, version,
msgType, false);
}
}
}
return msg;
}
private static int ParseGroup(IndexedStorage msg, MessageWithGroupDict messageWithGroupDict, int i, int tag,
TIntHashSet leadingTags, int level, FixVersionContainer version, string msgType, bool validation)
{
var delimiter = messageWithGroupDict.GetDelimiterTag(tag);
msg.StartCreateRg(tag, delimiter, i);
var groupTags = messageWithGroupDict.GetGroupTags(tag);
var allGroupTags = messageWithGroupDict.GetAllGroupTags();
i++;
var tagForAdd = msg.GetTagIdAtIndex(i);
if (tagForAdd != delimiter)
{
throw new InvalidDelimiterTagException(delimiter, tagForAdd, version, msgType);
}
while (i < msg.Count && groupTags.Contains(tagForAdd))
{
if (leadingTags.Contains(tagForAdd) && msg.GetTagValueAsLongAtIndex(i) > 0)
{
i = ParseGroup(msg, messageWithGroupDict, i, tagForAdd, leadingTags, ++level, version, msgType,
validation);
if (i < msg.Count)
{
tagForAdd = msg.GetTagIdAtIndex(i);
}
else
{
break;
}
}
else
{
msg.AddTagToRg(tagForAdd, i, tag);
i++;
if (i < msg.Count)
{
tagForAdd = msg.GetTagIdAtIndex(i);
}
else
{
break;
}
}
}
if (validation && level == 0)
{
if (allGroupTags.Contains(tagForAdd))
{
throw new UnexpectedGroupTagException(tagForAdd, version, msgType);
}
}
msg.StopCreateRg();
return i;
}
public static FixMessage GetFixMessage(string message)
{
return GetFixMessage(message.AsByteArray());
}
/// <summary>
/// Parses fix message from array of bytes.
/// </summary>
/// <param name="message"> the buffer of bytes </param>
/// <exception cref="GarbledMessageException"> if message is garbled </exception>
public static FixMessage GetFixMessage(byte[] message)
{
return GetFixMessage(message, 0, message.Length, DefaultRawTags, false, false);
}
internal static FixMessage GetFixMessage(byte[] message, bool usePool, bool isUserOwned)
{
return GetFixMessage(message, 0, message.Length, DefaultRawTags, usePool, isUserOwned);
}
/// <summary>
/// Parses fix message from array of bytes.
/// </summary>
/// <param name="message"> the buffer of bytes </param>
/// <exception cref="GarbledMessageException"> if message is garbled </exception>
internal static FixMessage GetFixMessageUntilTagsExists(byte[] message)
{
return GetFixMessageUntilTagsExists(message, 0, message.Length, DefaultRawTags);
}
/// <summary>
/// Parses fix message from array of bytes.
/// </summary>
/// <param name="message"> the buffer of bytes </param>
/// <param name="rawTags"> the raw tags </param>
/// <exception cref="GarbledMessageException"> if message is garbled </exception>
internal static FixMessage GetFixMessage(byte[] message, int[] rawTags)
{
return GetFixMessage(message, new CustomRawTags(rawTags));
}
internal static FixMessage GetFixMessage(byte[] message, IRawTags rawTags)
{
return GetFixMessage(message, 0, message.Length, rawTags);
}
/// <summary>
/// Parses fix message from array of bytes.
/// </summary>
/// <param name="message"> the buffer of bytes </param>
/// <param name="rawTags"> the raw tags </param>
/// <exception cref="GarbledMessageException"> if message is garbled </exception>
internal static FixMessage GetFixMessage(byte[] message, int messageOffset, int messageLength, int[] rawTags)
{
return GetFixMessage(message, messageOffset, messageLength, new CustomRawTags(rawTags));
}
internal static FixMessage GetFixMessage(byte[] message, int messageOffset, int messageLength,
IRawTags rawTags)
{
return GetFixMessage(message, messageOffset, messageLength, rawTags, false, false);
}
/// <summary>
/// Parses fix message from array of bytes.
/// </summary>
/// <param name="message"> the buffer of bytes </param>
/// <param name="rawTags"> the raw tags </param>
/// <returns> instance of parsed message </returns>
/// <exception cref="GarbledMessageException"> if message is garbled </exception>
internal static FixMessage GetFixMessage(byte[] message, int messageOffset, int messageLength,
IRawTags rawTags, bool allocateFromPool, bool isUserOwned)
{
FixMessage list;
if (allocateFromPool)
{
list = GetFixMessageFromPool(isUserOwned);
}
else
{
list = new FixMessage(isUserOwned);
}
return GetFixMessage(list, message, messageOffset, messageLength, rawTags, false);
}
internal static FixMessage GetFixMessage(FixMessage list, byte[] message, int messageOffset,
int messageLength, IRawTags rawTags)
{
return GetFixMessage(list, message, messageOffset, messageLength, rawTags, true);
}
internal static FixMessage GetFixMessage(FixMessage list, byte[] message, int messageOffset,
int messageLength, IRawTags rawTags, bool clearTheMessageBeforeUse)
{
if (clearTheMessageBeforeUse)
{
((AbstractFixMessage)list).Clear();
}
list.SetBuffer(message, messageOffset, messageLength);
var valueStartIndex = 0;
var tag = 0;
var isTagParsing = true;
var messageEndOffset = messageOffset + messageLength;
for (var index = messageOffset; index < messageEndOffset; index++)
{
var b = message[index];
if (isTagParsing)
{
if (b >= (byte)'0' && b <= (byte)'9')
{
tag = tag * 10 + (b - '0');
}
else if (b == (byte)'=')
{
valueStartIndex = index + 1;
if (rawTags.IsWithinRawTags(tag))
{
index += GetRawTagLengthFromPreviousField(list);
}
isTagParsing = false;
}
else
{
throw new GarbledMessageException("Invalid tag number");
}
}
else
{
if (b == (byte)'\u0001')
{
list.Add(tag, valueStartIndex, index - valueStartIndex);
tag = 0;
isTagParsing = true;
}
}
}
if (!isTagParsing || tag != 0)
{
throw new GarbledMessageException("No SOH symbol at the end of message");
}
return list;
}
/// <summary>
/// Parses fix message from array of bytes.
/// </summary>
/// <param name="message"> the buffer of bytes </param>
/// <param name="rawTags"> the raw tags </param>
/// <returns> instance of parsed message </returns>
/// <exception cref="GarbledMessageException"> if message is garbled </exception>
internal static FixMessage GetFixMessageUntilTagsExists(byte[] message, int messageOffset, int messageLength,
IRawTags rawTags)
{
var list = new FixMessage();
var valueStartIndex = 0;
var tag = 0;
var isTagParsing = true;
var messageEndOffset = messageOffset + messageLength;
for (var index = messageOffset; index < messageEndOffset; index++)
{
var b = message[index];
if (isTagParsing)
{
if (b >= (byte)'0' && b <= (byte)'9')
{
tag = tag * 10 + (b - '0');
}
else if (b == (byte)'=')
{
valueStartIndex = index + 1;
if (rawTags.IsWithinRawTags(tag))
{
index += GetRawTagLengthFromPreviousField(list);
}
isTagParsing = false;
}
else
{
break;
}
}
else
{
if (b == (byte)'\x0001')
{
list.AddTag(tag, CopyValue(message, valueStartIndex, index - valueStartIndex));
tag = 0;
isTagParsing = true;
}
}
}
if (!isTagParsing || tag != 0)
{
throw new GarbledMessageException("No SOH symbol at the end of message");
}
return list;
}
internal static TagValue GetFieldFromPool()
{
try
{
return FieldPool.Object;
}
catch (Exception e)
{
if (Log.IsDebugEnabled)
{
Log.Warn("Can't get new object from pool: " + e.Message, e);
}
else
{
Log.Warn("Can't get new object from pool: " + e.Message);
}
}
return null;
}
internal static FixMessage GetFixMessageFromPool(bool isUserOwned)
{
try
{
var o = FieldListPool.Object;
o.IsUserOwned = isUserOwned;
o.IsFree = false;
return o;
}
catch (Exception e)
{
if (Log.IsDebugEnabled)
{
Log.Warn("Can't get new object from pool: " + e.Message, e);
}
else
{
Log.Warn("Can't get new array object pool: " + e.Message);
}
}
return null;
}
internal static void ReturnObj(TagValue field)
{
FieldPool.ReturnObject(field);
}
internal static void ReturnObj(FixMessage fixMessage)
{
if (fixMessage.IsOriginatingFromPool)
{
((AbstractFixMessage)fixMessage).Clear();
fixMessage.IsFree = true;
FieldListPool.ReturnObject(fixMessage);
}
}
internal static int FieldObjectsCreated => FieldPool.ObjectsCreated;
internal static int FieldListObjectsCreated => FieldListPool.ObjectsCreated;
internal static byte[] CopyValueUsePool(byte[] message, int valueStartIndex, int length)
{
var value = ByteArrayPool.GetByteArrayFromPool(length);
Array.Copy(message, valueStartIndex, value, 0, length);
return value;
}
internal static byte[] CopyValue(byte[] message, int valueStartIndex, int length)
{
var value = new byte[length];
Array.Copy(message, valueStartIndex, value, 0, length);
return value;
}
internal static int GetRawTagLengthFromPreviousField(FixMessage list)
{
try
{
return (int)list.GetTagValueAsLongAtIndex(list.Length - 1);
}
catch (Exception)
{
throw new GarbledMessageException("Invalid or missing raw tag length");
}
}
/// <summary>
/// Gets the message type.
/// If type is unknown return empty array.
/// </summary>
/// <param name="bytes"> the message </param>
internal static byte[] GetMessageType(byte[] bytes)
{
var tagValue = GetRawValue(bytes, 0, bytes.Length, Tags.MsgType, true);
return tagValue ?? EmptyValue;
}
/// <summary>
/// Gets the message type.
/// If type is unknown return empty array.
/// </summary>
/// <param name="bytes"> the message </param>
/// <param name="offset">the offset in buffer </param>
/// <param name="length">the buffer length </param>
internal static byte[] GetMessageType(byte[] bytes, int offset, int length)
{
var tagValue = GetRawValue(bytes, offset, length, Tags.MsgType, true);
return tagValue ?? EmptyValue;
}
/// <summary>
/// Checks the message session level type.
/// </summary>
/// <param name="message"> the fix message </param>
/// <returns> true if message is level session message </returns>
internal static bool IsSessionLevelMessage(byte[] message, int offset, int length)
{
var bytes = GetMessageType(message, offset, length);
return bytes != null && IsSessionLevelType(bytes);
}
/// <summary>
/// Checks the message session level type.
/// </summary>
/// <param name="message"> the fix message </param>
/// <returns> true if message is level session message </returns>
internal static bool IsSessionLevelMessage(byte[] message)
{
var bytes = GetMessageType(message);
return bytes != null && IsSessionLevelType(bytes);
}
internal static bool IsSessionLevelMessage(FixMessage message)
{
try
{
var length = message.GetTagLength(35);
var msgType = message.GetTagValueAsByte(35, 0);
return IsSessionLevelType(msgType, length);
}
catch (FieldNotFoundException)
{
}
return false;
}
/// <summary>
/// Checks the message session level type.
/// </summary>
/// <param name="msgType"> the message type </param>
/// <returns> true if message is level session message </returns>
internal static bool IsSessionLevelType(string msgType)
{
if (msgType.Length == 1)
{
var ch = msgType[0];
if (ch == 'A' || ch >= '0' && ch <= '5')
{
return true;
}
}
return false;
}
/// <summary>
/// Checks the message session level type.
/// </summary>
/// <param name="msgType"> the fix message </param>
/// <returns> true if message is level session message </returns>
internal static bool IsSessionLevelType(byte[] msgType)
{
if (msgType.Length == 1)
{
var b = msgType[0];
if (b == (byte)'A' || b >= (byte)'0' && b <= (byte)'5')
{
return true;
}
}
return false;
}
internal static bool IsSessionLevelType(byte msgType, int length)
{
if (length == 1)
{
if (msgType == (byte)'A' || msgType >= (byte)'0' && msgType <= (byte)'5')
{
return true;
}
}
return false;
}
/// <summary>
/// Checks if message type is Logon.
/// </summary>
/// <param name="msgType"> the message type, parameter must be not null </param>
/// <returns> true if is </returns>
internal static bool IsLogon(string msgType)
{
return msgType.Length == 1 && msgType[0] == 'A';
}
/// <summary>
/// Checks if message type is Logon.
/// </summary>
/// <param name="msgType"> the message type, parameter must be not null </param>
/// <returns> true if is </returns>
internal static bool IsLogon(byte[] msgType)
{
return msgType.Length == 1 && msgType[0] == LogonBytes[0];
}
/// <summary>
/// Checks if message type is Logon.
/// </summary>
/// <param name="fixMessage"> fix field list, parameter must be not null </param>
/// <returns> true if is </returns>
internal static bool IsLogon(FixMessage fixMessage)
{
return fixMessage.IsTagValueEqual(Tags.MsgType, LogonBytes);
}
/// <summary>
/// Creates RawTags from array of int.
/// </summary>
/// <param name="rawTags"> </param>
/// <returns> RawTags </returns>
internal static IRawTags CreateRawTags(int[] rawTags)
{
var customRawTags = new CustomRawTags(rawTags);
if (customRawTags.GetRawTags().SequenceEqual(Message.DefaultRawTags.DefaultRawTagsSorted))
{
return DefaultRawTags;
}
return customRawTags;
}
/// <summary>
/// Creates RawTags from string. The raw tags should be separate by ',' , ' ' or '.'.
/// </summary>
/// <param name="rawTags"> </param>
/// <returns> RawTags </returns>
internal static IRawTags CreateRawTags(string rawTags)
{
if (string.IsNullOrEmpty(rawTags))
{
// if empty value
return DefaultRawTags;
}
var tokens = rawTags.Split(new[] { ',', ' ', '.' }, StringSplitOptions.RemoveEmptyEntries);
var rawTagsArray = new int[tokens.Length];
for (var i = 0; i < tokens.Length; i++)
{
try
{
rawTagsArray[i] = int.Parse(tokens[i]);
}
catch (Exception)
{
rawTagsArray[i] = 0;
}
}
return CreateRawTags(rawTagsArray);
}
private class TagValueProvider : AbstractPoolableProvider<TagValue>
{
public override TagValue Create()
{
return new TagValue(true);
}
public override void Activate(TagValue t)
{
}
}
private class FixMessageProvider : AbstractPoolableProvider<FixMessage>
{
public override FixMessage Create()
{
var list = new FixMessage
{
IsOriginatingFromPool = true
};
return list;
}
public override void Activate(FixMessage t)
{
}
}
internal interface IRawTags
{
/// <summary>
/// Checks if tag exist in array of tags.
/// </summary>
/// <param name="tag"> the tag id </param>
/// <returns> true if exists </returns>
bool IsWithinRawTags(int tag);
}
}
}