FixAntenna/NetCore/Message/IndexedStorage.cs (1,479 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.Utils; using Epam.FixAntenna.NetCore.Configuration; using Epam.FixAntenna.NetCore.Helpers; using Epam.FixAntenna.NetCore.Message.Format; using Epam.FixAntenna.NetCore.Message.Rg; using Epam.FixAntenna.NetCore.Message.SpecialTags; using Epam.FixAntenna.NetCore.Message.Storage; namespace Epam.FixAntenna.NetCore.Message { public class IndexedStorage { public enum MissingTagHandling { AddIfNotExists, AlwaysAdd, DontAddIfNotExists } public const int NotFound = FieldIndex.Notfound; //Must be power of two internal const int EnlargeTablesByRatio = 2; protected internal const char FieldSeparator = '\u0001'; //TODO: get rid private const byte SOH = 0x01; /// <summary> /// internal independent storage. Used for new values or for making instance standalone /// </summary> /// <seealso cref="_origBuffer"></seealso> private readonly ArenaMessageStorage _arenaStorage; private readonly FieldIndex _fieldsIndex; /// <summary> /// link to the original buffer which is external for messages. /// </summary> /// <seealso cref="_arenaStorage"> </seealso> private readonly ByteArrayMessageStorage _origBuffer; private readonly PerFieldMessageStorage _perFieldStorage; private readonly HashSet<int> _unserializableTags = new HashSet<int>(); /// <summary> /// Indicate that yet not allocated new space for tag and all values in original place /// </summary> private bool _continuousBuffer = true; private FixVersionContainer _fixVersion; private RepeatingGroupStorage _repeatingGroupStorage; public IndexedStorage(int initialSize) { _fieldsIndex = new FieldIndex(); _perFieldStorage = new PerFieldMessageStorage(initialSize); _arenaStorage = new ArenaMessageStorage(); _origBuffer = new ByteArrayMessageStorage(); _unserializableTags = new HashSet<int>(Enumerable.Range(0, 32).ToList()); _unserializableTags.Clear(); } //------------- ADD tag methods ----------------// public int AddTagAtIndex(int addAtIndex, int tagId, byte value) { //TBD! implement in right way! var bytes = new[] { value }; return AddTagAtIndex(addAtIndex, tagId, bytes, 0, 1); } public int AddTag(int tagId, char value) { //TBD! implement in right way! return AddTag(tagId, new []{(byte)value}, 0, 1); } public int AddTagAtIndex(int addAtIndex, int tagId, char value) { //TBD! implement in right way! return AddTagAtIndex(addAtIndex, tagId, (byte)value); } public int AddTag(int tag, byte[] value, int offset, int length) { return UpdateValue(tag, value, offset, length, MissingTagHandling.AlwaysAdd); } internal virtual RepeatingGroupStorage GetRepeatingGroupStorage() { return _repeatingGroupStorage; } public int AddTagAtIndex(int addAtIndex, int tagId, byte[] value, int offset, int length) { var fieldCount = ReserveTagAtIndex(addAtIndex, tagId); UpdateValueAtIndex(addAtIndex, value, offset, length); return fieldCount; } public int AddTag(int tag, long value) { return UpdateValue(tag, value, MissingTagHandling.AlwaysAdd); } public int AddTagAtIndex(int index, int tagId, long value) { return AddTagAtIndex(index, tagId, value, true); } public int AddTagAtIndex(int index, int tagId, long value, bool shiftRg) { var fieldCount = ReserveTagAtIndex(index, tagId, shiftRg); UpdateValueAtIndex(index, value); return fieldCount; } protected internal int AddTagAtIndexForRg(int index, int tagId, long value, int rgId) { var fieldCount = ReserveTagAtIndexForRg(index, tagId, rgId); UpdateValueAtIndex(index, value); return fieldCount; } public int AddTag(int tag, double value, int precision) { return UpdateValue(tag, value, precision, MissingTagHandling.AlwaysAdd); } public int AddTagAtIndex(int index, int tagId, double value, int precision) { var fieldCount = ReserveTagAtIndex(index, tagId); UpdateValueAtIndex(index, value, precision); return fieldCount; } public int AddTag(int tag, string value) { return UpdateValue(tag, value.AsByteArray(), MissingTagHandling.AlwaysAdd); } public int AddTagAtIndex(int index, int tagId, string value) { var fieldCount = ReserveTagAtIndex(index, tagId); UpdateValueAtIndex(index, value.AsByteArray()); return fieldCount; } public int AddCalendarTag(int tag, DateTimeOffset value, FixDateFormatterFactory.FixDateType type) { return UpdateCalendarValue(tag, value, type, MissingTagHandling.AlwaysAdd); } public int AddCalendarTagAtIndex(int index, int tagId, DateTimeOffset value, FixDateFormatterFactory.FixDateType type) { var fieldCount = ReserveTagAtIndex(index, tagId); UpdateCalendarValueAtIndex(index, value, type); return fieldCount; } public int AddTimeTag(int tag, DateTime value, TimestampPrecision precision) { if (precision == TimestampPrecision.Minute) { throw new ArgumentException("Invalid value of the desired precision: " + precision); } return UpdateTimeValue(tag, value, precision, MissingTagHandling.AlwaysAdd); } public int AddTimeTagAtIndex(int index, int tagId, DateTime value, TimestampPrecision precision) { var fieldCount = ReserveTagAtIndex(index, tagId); if (precision == TimestampPrecision.Minute) { throw new ArgumentException("Invalid value of the desired precision: " + precision); } UpdateTimeValueAtIndex(index, value, precision); return fieldCount; } public int AddTimeTag(int tag, DateTimeOffset value, TimestampPrecision precision) { return UpdateTimeValue(tag, value, precision, MissingTagHandling.AlwaysAdd); } public int AddTimeTagAtIndex(int index, int tagId, DateTimeOffset value, TimestampPrecision precision) { var fieldCount = ReserveTagAtIndex(index, tagId); UpdateTimeValueAtIndex(index, value, precision); return fieldCount; } public int AddDateTimeTag(int tag, DateTime value, TimestampPrecision precision) { if (precision == TimestampPrecision.Minute) { throw new ArgumentException("Invalid value of the desired precision: " + precision); } return UpdateDateTimeValue(tag, value, precision, MissingTagHandling.AlwaysAdd); } public int AddDateTimeTagAtIndex(int index, int tagId, DateTime value, TimestampPrecision precision) { var fieldCount = ReserveTagAtIndex(index, tagId); if (precision == TimestampPrecision.Minute) { throw new ArgumentException("Invalid value of the desired precision: " + precision); } UpdateDateTimeValueAtIndex(index, value, precision); return fieldCount; } public int AddDateTimeTag(int tag, DateTimeOffset value, TimestampPrecision precision) { return UpdateDateTimeValue(tag, value, precision, MissingTagHandling.AlwaysAdd); } public int AddDateTimeTagAtIndex(int index, int tagId, DateTimeOffset value, TimestampPrecision precision) { var fieldCount = ReserveTagAtIndex(index, tagId); UpdateDateTimeValueAtIndex(index, value, precision); return fieldCount; } /// <summary> /// Add or update tag with padded long value. /// </summary> /// <param name="tag">Tag ID to process.</param> /// <param name="value">Tag value.</param> /// <param name="padding">Tag value padding.</param> /// <param name="addIfNotExists"><see cref="MissingTagHandling"/> - defines what to do if the tag is not present in the message.</param> /// <returns>Returns index of the processed tag, or -1 if tag not found and <paramref name="addIdNotExist"/> is <see cref="MissingTagHandling.DontAddIfNotExists"/></returns> public int SetPaddedLongTag(int tag, long value, int padding, MissingTagHandling addIfNotExists = MissingTagHandling.AlwaysAdd) { var index = FindOrPrepareToAdd(tag, 0, addIfNotExists); if (index == FieldIndex.Notfound) return index; _fieldsIndex.CheckTagExistsAtIndex(index); var oldLen = _fieldsIndex.GetLength(index); var newLength = FixTypes.FormatIntLengthWithPadding(value, padding); if (CanCopyInPlaceNumber(index, oldLen, newLength)) { var length = newLength > oldLen ? newLength : oldLen; _fieldsIndex.UpdateLength(index, length); GetStorage(index).UpdateValue(index, _fieldsIndex, value); } else { _fieldsIndex.UpdateLength(index, newLength); var stg = NewStorageForEntry(index, newLength); stg.SetPaddedValue(index, value, newLength); } return index; } protected internal virtual int ReserveTagAtIndexForRg(int addAtIndex, int tagId, int rgId) { EnsureCapacityAndEnlarge(); _fieldsIndex.AddAtIndex(addAtIndex, tagId, 0); var fieldCount = _fieldsIndex.Count; _perFieldStorage.Shift(addAtIndex, 1, fieldCount); return fieldCount; } public virtual int ReserveTagAtIndex(int addAtIndex, int tagId) { return ReserveTagAtIndex(addAtIndex, tagId, true); } public virtual int ReserveTagAtIndex(int addAtIndex, int tagId, bool shiftRg) { EnsureCapacityAndEnlarge(); _fieldsIndex.AddAtIndex(addAtIndex, tagId, 0); var fieldCount = _fieldsIndex.Count; _perFieldStorage.Shift(addAtIndex, 1, fieldCount); if (shiftRg && _repeatingGroupStorage != null && !_repeatingGroupStorage.IsInvalidated) { _repeatingGroupStorage.Shift(addAtIndex, 1, -1, -1, true); } return fieldCount; } //------------- GET tag methods ----------------// #region LoadTagValue methods public void LoadTagValue(int tagId, TagValue destination) { var index = GetTagIndex(tagId); if (index != FieldIndex.Notfound) { ReloadTagValue(index, destination); } else { throw new FieldNotFoundException("Field (tagId=" + tagId + ") not found"); } } public void LoadTagValue(int tagId, TagValue destination, int occurrence) { var index = GetTagIndex(tagId, occurrence); if (index != FieldIndex.Notfound) { ReloadTagValue(index, destination); } else { throw new FieldNotFoundException("Field (tagId=" + tagId + ") not found"); } } public void LoadTagValueByIndex(int index, TagValue destination) { _fieldsIndex.CheckTagExistsAtIndex(index); ReloadTagValue(index, destination); } #endregion public byte[] GetTagValueAsBytesAtIndex(int index) { var res = new byte[GetTagValueLengthAtIndex(index)]; GetTagValueAsBytesAtIndex(index, res, 0); return res; } public int GetTagValueAsBytesAtIndex(int index, byte[] dest, int offset) { _fieldsIndex.CheckTagExistsAtIndex(index); return GetStorage(index).GetAsByteArray(index, FieldIndexData, dest, offset); } public byte GetTagValueAsByteAtIndex(int index, int offset) { _fieldsIndex.CheckTagExistsAtIndex(index); return GetStorage(index).GetAsByte(index, FieldIndexData, offset); } public bool GetTagValueAsBoolAtIndex(int index) { _fieldsIndex.CheckTagExistsAtIndex(index); return GetStorage(index).GetAsBoolean(index, FieldIndexData); } public double GetTagValueAsDoubleAtIndex(int index) { _fieldsIndex.CheckTagExistsAtIndex(index); return GetStorage(index).GetAsDouble(index, FieldIndexData); } public decimal GetTagValueAsDecimalAtIndex(int index) { _fieldsIndex.CheckTagExistsAtIndex(index); var decimalInString = GetStorage(index).GetAsString(index, FieldIndexData); return decimal.Parse(decimalInString, FixTypes.UsFormatSymbols); } public long GetTagValueAsLongAtIndex(int index) { _fieldsIndex.CheckTagExistsAtIndex(index); return GetStorage(index).GetAsLong(index, FieldIndexData); } public void GetTagValueAsStringBuffAtIndex(int index, StringBuilder str) { _fieldsIndex.CheckTagExistsAtIndex(index); GetStorage(index).GetAsStringBuffer(index, FieldIndexData, str); } internal void GetTagValueAsReusableStringAtIndex(ReusableString reusableString, int index) { _fieldsIndex.CheckTagExistsAtIndex(index); GetStorage(index).GetAsReusableString(index, FieldIndexData, reusableString); } public virtual string GetTagValueAsStringAtIndex(int index) { _fieldsIndex.CheckTagExistsAtIndex(index); return GetStorage(index).GetAsString(index, FieldIndexData); } internal void GetTagValueAtIndex(int index, ByteBuffer destination) { _fieldsIndex.CheckTagExistsAtIndex(index); AddTagValueToBuffer(index, destination); } private void AddTagValueToBuffer(int index, ByteBuffer destination) { var array = GetStorage(index).GetByteArray(index); var offset = _fieldsIndex.GetOffset(index); var length = _fieldsIndex.GetLength(index); destination.Add(array, offset, length); } //------------- Update tag methods ----------------// public int UpdateValue(int tag, byte[] value, int offset, int length, MissingTagHandling addIfNotExists) { return UpdateValue(tag, 0, value, offset, length, addIfNotExists); } public int UpdateValue(int tag, int occurrence, byte[] value, int offset, int length, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateValueAtIndex(index, value, offset, length); } return index; } public virtual void UpdateValueAtIndex(int tagIndex, byte[] value, int offset, int length) { _fieldsIndex.CheckTagExistsAtIndex(tagIndex); var oldLen = _fieldsIndex.GetLength(tagIndex); if (CanCopyInPlace(tagIndex, oldLen, length)) { GetStorage(tagIndex).UpdateValue(tagIndex, _fieldsIndex, value.AsSpan(offset, length)); } else { _fieldsIndex.UpdateLength(tagIndex, length); var stg = NewStorageForEntry(tagIndex, length); stg.Add(tagIndex, value, offset, length); } } public int UpdateValue(int tag, byte[] value, MissingTagHandling addIfNotExists) { return UpdateValue(tag, value, 0, value.Length, addIfNotExists); } public int UpdateValue(int tagId, int occurrence, byte[] value, MissingTagHandling addIfNotExists) { return UpdateValue(tagId, occurrence, value, 0, value.Length, addIfNotExists); } public virtual void UpdateValueAtIndex(int index, byte[] value) { UpdateValueAtIndex(index, value, 0, value.Length); } public int UpdateValue(int tag, long value, MissingTagHandling addIfNotExists) { return UpdateValue(tag, 0, value, addIfNotExists); } public int UpdateValue(int tag, int occurrence, long value, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateValueAtIndex(index, value); } return index; } public virtual void UpdatePaddedValueAtIndex(int index, long value, int padding) { _fieldsIndex.CheckTagExistsAtIndex(index); var oldLen = _fieldsIndex.GetLength(index); var newLength = FixTypes.FormatIntLengthWithPadding(value, padding); if (CanCopyInPlaceNumber(index, oldLen, newLength)) { var length = newLength > oldLen ? newLength : oldLen; _fieldsIndex.UpdateLength(index, length); GetStorage(index).UpdateValue(index, _fieldsIndex, value); } else { _fieldsIndex.UpdateLength(index, newLength); var stg = NewStorageForEntry(index, newLength); stg.SetPaddedValue(index, value, newLength); } } public virtual void UpdateValueAtIndex(int index, long value) { _fieldsIndex.CheckTagExistsAtIndex(index); var oldLen = _fieldsIndex.GetLength(index); var newLength = FixTypes.FormatIntLength(value); if (CanCopyInPlaceNumber(index, oldLen, newLength)) { var length = newLength > oldLen ? newLength : oldLen; _fieldsIndex.UpdateLength(index, length); GetStorage(index).UpdateValue(index, _fieldsIndex, value); } else { _fieldsIndex.UpdateLength(index, newLength); var stg = NewStorageForEntry(index, newLength); stg.SetValue(index, value, newLength); } } public int UpdateValue(int tag, double value, int precision, MissingTagHandling addIfNotExists) { return UpdateValue(tag, 0, value, precision, addIfNotExists); } public int UpdateValue(int tag, int occurrence, double value, int precision, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateValueAtIndex(index, value, precision); } return index; } public virtual void UpdateValueAtIndex(int index, double value, int precision) { _fieldsIndex.CheckTagExistsAtIndex(index); var oldLen = _fieldsIndex.GetLength(index); //TODO: fix getFormatLength to return the right lenth for rounded with truncation doubles (0.099999 -> 0.1) and fix the rest code var newLength = DoubleFormatter.GetFormatLength(value, precision); if (CanCopyInPlaceNumber(index, oldLen, newLength)) { var length = newLength > oldLen ? newLength : oldLen; _fieldsIndex.UpdateLength(index, length); GetStorage(index).UpdateValue(index, _fieldsIndex, value, precision); } else { var stg = NewStorageForEntry(index, newLength); var realLength = stg.SetValue(index, value, precision, newLength); _fieldsIndex.UpdateLength(index, realLength); } } public int UpdateValue(int tag, string strBuffer, MissingTagHandling addIfNotExists) { return UpdateValue(tag, 0, strBuffer, addIfNotExists); } public int UpdateValue(int tag, int occurrence, string strBuffer, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateValueAtIndex(index, strBuffer.AsByteArray()); } return index; } public virtual void UpdateValueAtIndex(int tagIndex, string str) { _fieldsIndex.CheckTagExistsAtIndex(tagIndex); var length = str.Length; var oldLen = _fieldsIndex.GetLength(tagIndex); MessageStorage stg = null; if (CanCopyInPlace(tagIndex, oldLen, length)) { length = oldLen; stg = GetStorage(tagIndex); } else if (CanCopyInPlaceWithLengthReducing(tagIndex, oldLen, length)) { _fieldsIndex.UpdateLength(tagIndex, length); stg = GetStorage(tagIndex); } else { _fieldsIndex.UpdateLength(tagIndex, length); stg = NewStorageForEntry(tagIndex, length); } var dstOffset = _fieldsIndex.GetOffset(tagIndex); stg.SetValue(tagIndex, str, dstOffset, length); } public int UpdateValue(int tag, bool value, MissingTagHandling addIfNotExists) { return UpdateValue(tag, 0, value, addIfNotExists); } public int UpdateValue(int tag, int occurrence, bool value, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateValueAtIndex(index, value); } return index; } public virtual void UpdateValueAtIndex(int tagIndex, bool value) { var formattedValue = FixTypes.FormatBoolean(value); UpdateValueAtIndex(tagIndex, formattedValue); } public int UpdateValue(TagValue value, MissingTagHandling addIfNotExists) { return UpdateValue(value, 0, addIfNotExists); } public int UpdateValue(TagValue value, int occurrence, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(value.TagId, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateValueAtIndex(index, value); } return index; } public virtual void UpdateValueAtIndex(int tagIndex, TagValue value) { UpdateValueAtIndex(tagIndex, value.Buffer, value.Offset, value.Length); } public int UpdateCalendarValue(int tag, DateTimeOffset value, FixDateFormatterFactory.FixDateType type, MissingTagHandling addIfNotExists) { return UpdateCalendarValue(tag, 0, value, type, addIfNotExists); } public int UpdateCalendarValue(int tag, int occurrence, DateTimeOffset value, FixDateFormatterFactory.FixDateType type, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateCalendarValueAtIndex(index, value, type); } return index; } public virtual void UpdateCalendarValueAtIndex(int index, DateTimeOffset value, FixDateFormatterFactory.FixDateType type) { _fieldsIndex.CheckTagExistsAtIndex(index); var fixDateFormatter = FixDateFormatterFactory.GetFixDateFormatter(type); var oldLen = _fieldsIndex.GetLength(index); var newLength = fixDateFormatter.GetFormattedStringLength(value); if (CanCopyInPlace(index, oldLen, newLength)) { var length = newLength > oldLen ? newLength : oldLen; _fieldsIndex.UpdateLength(index, length); GetStorage(index).UpdateCalendarValue(index, _fieldsIndex, fixDateFormatter, value); } else { _fieldsIndex.UpdateLength(index, newLength); var stg = NewStorageForEntry(index, newLength); stg.SetCalendarValue(index, fixDateFormatter, value, newLength); } } public int UpdateTimeValue(int tag, DateTime value, TimestampPrecision precision, MissingTagHandling addIfNotExists) { return UpdateTimeValue(tag, 0, value, precision, addIfNotExists); } public int UpdateTimeValue(int tag, int occurrence, DateTime value, TimestampPrecision precision, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateTimeValueAtIndex(index, value, precision); } return index; } public virtual void UpdateTimeValueAtIndex(int index, DateTime value, TimestampPrecision precision) { _fieldsIndex.CheckTagExistsAtIndex(index); var oldLen = _fieldsIndex.GetLength(index); var newLength = GetHpTimestampBufferLength(false, precision, 0); if (CanCopyInPlace(index, oldLen, newLength)) { var length = newLength > oldLen ? newLength : oldLen; _fieldsIndex.UpdateLength(index, length); GetStorage(index).UpdateTimeValue(index, _fieldsIndex, value, precision); } else { _fieldsIndex.UpdateLength(index, newLength); var stg = NewStorageForEntry(index, newLength); stg.SetTimeValue(index, value, precision, newLength); } } public int UpdateTimeValue(int tag, DateTimeOffset value, TimestampPrecision precision, MissingTagHandling addIfNotExists) { return UpdateTimeValue(tag, 0, value, precision, addIfNotExists); } public int UpdateTimeValue(int tag, int occurrence, DateTimeOffset value, TimestampPrecision precision, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateTimeValueAtIndex(index, value, precision); } return index; } public virtual void UpdateTimeValueAtIndex(int index, DateTimeOffset value, TimestampPrecision precision) { _fieldsIndex.CheckTagExistsAtIndex(index); var oldLen = _fieldsIndex.GetLength(index); var newLength = GetHpTimestampBufferLength(false, precision, GetTzLength(value.Offset)); if (CanCopyInPlace(index, oldLen, newLength)) { var length = newLength > oldLen ? newLength : oldLen; _fieldsIndex.UpdateLength(index, length); GetStorage(index).UpdateTimeValue(index, _fieldsIndex, value, precision); } else { _fieldsIndex.UpdateLength(index, newLength); var stg = NewStorageForEntry(index, newLength); stg.SetTimeValue(index, value, precision, newLength); } } public int UpdateDateTimeValue(int tag, DateTime value, TimestampPrecision precision, MissingTagHandling addIfNotExists) { return UpdateDateTimeValue(tag, 0, value, precision, addIfNotExists); } public int UpdateDateTimeValue(int tag, int occurrence, DateTime value, TimestampPrecision precision, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateDateTimeValueAtIndex(index, value, precision); } return index; } public virtual void UpdateDateTimeValueAtIndex(int index, DateTime value, TimestampPrecision precision) { _fieldsIndex.CheckTagExistsAtIndex(index); var oldLen = _fieldsIndex.GetLength(index); var newLength = GetHpTimestampBufferLength(true, precision, 0); if (CanCopyInPlace(index, oldLen, newLength)) { var length = newLength > oldLen ? newLength : oldLen; _fieldsIndex.UpdateLength(index, length); GetStorage(index).UpdateDateTimeValue(index, _fieldsIndex, value, precision); } else { _fieldsIndex.UpdateLength(index, newLength); var stg = NewStorageForEntry(index, newLength); stg.SetDateTimeValue(index, value, precision, newLength); } } public int UpdateDateTimeValue(int tag, DateTimeOffset value, TimestampPrecision precision, MissingTagHandling addIfNotExists) { return UpdateDateTimeValue(tag, 0, value, precision, addIfNotExists); } public int UpdateDateTimeValue(int tag, int occurrence, DateTimeOffset value, TimestampPrecision precision, MissingTagHandling addIfNotExists) { var index = FindOrPrepareToAdd(tag, occurrence, addIfNotExists); if (index != FieldIndex.Notfound) { UpdateDateTimeValueAtIndex(index, value, precision); } return index; } public virtual void UpdateDateTimeValueAtIndex(int index, DateTimeOffset value, TimestampPrecision precision) { _fieldsIndex.CheckTagExistsAtIndex(index); var oldLen = _fieldsIndex.GetLength(index); var newLength = GetHpTimestampBufferLength(true, precision, GetTzLength(value.Offset)); if (CanCopyInPlace(index, oldLen, newLength)) { var length = newLength > oldLen ? newLength : oldLen; _fieldsIndex.UpdateLength(index, length); GetStorage(index).UpdateDateTimeValue(index, _fieldsIndex, value, precision); } else { _fieldsIndex.UpdateLength(index, newLength); var stg = NewStorageForEntry(index, newLength); stg.SetDateTimeValue(index, value, precision, newLength); } } private int GetTzLength(TimeSpan zoneOffset) { var offsetMinutes = zoneOffset.GetTotalMinutes(); if (offsetMinutes == 0) { return 1; } if (offsetMinutes % 60 == 0) { return 3; } return 6; } private int GetHpTimestampBufferLength(bool date, TimestampPrecision precision, int tz) { // add bytes for time zone var bufLength = tz; // add bytes for time switch (precision) { case TimestampPrecision.Minute: { bufLength += 5; break; } case TimestampPrecision.Second: { bufLength += 8; break; } case TimestampPrecision.Milli: { bufLength += 12; break; } case TimestampPrecision.Micro: { bufLength += 15; break; } case TimestampPrecision.Nano: { bufLength += 18; break; } } // add bytes for date if (date) { bufLength += 9; } return bufLength; } //------------- FIND tag methods ----------------// public virtual bool IsTagExists(int tag) { return _fieldsIndex.FindIndexEntryInHashTbl(tag) != FieldIndex.Notfound; } public virtual bool IsTagExists(int tag, int occurrence) { var tagIndex = GetTagIndex(tag, occurrence); return tagIndex != NotFound; } public virtual int GetTagIndex(int tag) { return _fieldsIndex.GetTagIndex(tag); } public virtual int GetTagIndex(int tag, int occurrence) { return _fieldsIndex.GetTagOccurrenceIndex(tag, occurrence); } public int GetTagIndexStartingFrom(int tag, int fromIndex) { return GetTagIndexBetween(tag, fromIndex, Count); } public int GetTagIndexBetween(int tag, int startIndex, int endIndex) { return _fieldsIndex.GetTagIndex(tag, startIndex, endIndex); } public virtual int GetTagIdAtIndex(int index) { _fieldsIndex.CheckTagExistsAtIndex(index); return _fieldsIndex.GetTag(index); } public virtual int GetTagValueLengthAtIndex(int index) { _fieldsIndex.CheckTagExistsAtIndex(index); return _fieldsIndex.GetLength(index); } protected internal virtual int GetTagValueOffsetAtIndex(int index) { _fieldsIndex.CheckTagExistsAtIndex(index); return _fieldsIndex.GetOffset(index); } public virtual int GetTagValueLength(int tagId) { var index = GetTagIndex(tagId); if (index == FieldIndex.Notfound) { throw new FieldNotFoundException("Field (tag=" + tagId + ") not found"); } return _fieldsIndex.GetLength(index); } public virtual int GetTagValueLength(int tagId, int occurrence) { var index = GetTagIndex(tagId, occurrence); if (index == FieldIndex.Notfound) { throw new FieldNotFoundException("Field (tag=" + tagId + ") not found"); } return _fieldsIndex.GetLength(index); } //------------- Message info methods ----------------// public virtual int GetNumOfGroup() { return 0; } public virtual int Count => _fieldsIndex.Count; public int RawLength { get { var length = 0; //TBD! optimize var msgSize = _fieldsIndex.Count; for (var i = 0; i < msgSize; i++) { var tag = _fieldsIndex.GetTag(i); if (IsUnserializableTag(tag)) { continue; } length += GetTagBytesLength(tag) + _fieldsIndex.GetLength(i) + 1 + 1; // + '=' + 'SOH' } return length; } } internal virtual IFieldIndexData FieldIndexData => _fieldsIndex; protected internal virtual bool IsAllTagsInOneBuffer => _continuousBuffer && _fieldsIndex.CheckTagsStorageType(FieldIndex.FlagOrigbufStorage | FieldIndex.FlagArenaStorage); //------------- Message manipulation methods ----------------// protected internal virtual void FillSubStorage(int fromIndex, int toIndex, IndexedStorage subStorage) { var size = _fieldsIndex.Count; if (fromIndex < 0 || fromIndex > size || toIndex < 0 || toIndex > size) { throw new IndexOutOfRangeException("Invalid bounds for new list ([" + fromIndex + ":" + toIndex + "]). The current index size is " + size); } subStorage.SetOriginalBuffer(_origBuffer.Buffer, _origBuffer.Offset, _origBuffer.Length); //TODO: may be we can use isAllTagsInOneBuffer for (var index = fromIndex; index <= toIndex; index++) { var buffer = GetStorage(index).GetByteArray(index); var valueOffset = _fieldsIndex.GetOffset(index); var valueLength = _fieldsIndex.GetLength(index); var tagId = _fieldsIndex.GetTag(index); subStorage.UpdateValue(tagId, buffer, valueOffset, valueLength, MissingTagHandling.AlwaysAdd); } } public virtual void Clear() { _fieldsIndex.Clear(); _origBuffer.ClearAll(); _arenaStorage.ClearAll(); _perFieldStorage.ClearAll(); _continuousBuffer = true; InvalidateRepeatingGroupIndex(); } /// <summary> /// Removes a fix field with specified tag from collection. /// The methods removes the first occurrence of the specified tag. /// </summary> /// <param name="tag"> the fix tag. </param> /// <returns> <c>true</c> if the element was removed. </returns> public virtual bool RemoveTag(int tag) { var index = GetTagIndex(tag); if (index == FieldIndex.Notfound) { return false; } return RemoveTagAtIndex(index); } public virtual bool RemoveTag(int tag, int occurrence) { var index = GetTagIndex(tag, occurrence); if (index == FieldIndex.Notfound) { return false; } return RemoveTagAtIndex(index); } public virtual bool RemoveTagAtIndex(int tagIndex) { return RemoveTagAtIndex(tagIndex, true); } public virtual bool RemoveTagAtIndex(int tagIndex, bool shiftRg) { _fieldsIndex.CheckTagExistsAtIndex(tagIndex); _fieldsIndex.RemoveFromHashtbl(tagIndex); RemoveElementFromIndex(tagIndex, shiftRg); return true; } protected internal virtual void DeepCopy(IndexedStorage source) { _origBuffer.Copy(source._origBuffer); _arenaStorage.Copy(source._arenaStorage); _perFieldStorage.Copy(source._perFieldStorage); _fieldsIndex.DeepCopyFrom(source._fieldsIndex); if (source._repeatingGroupStorage != null && !source._repeatingGroupStorage.IsInvalidated) { _repeatingGroupStorage = source._repeatingGroupStorage.Copy(this); } _continuousBuffer = source._continuousBuffer; _fixVersion = source._fixVersion; } protected internal virtual void EnsureCapacityAndEnlarge() { EnsureCapacityAndEnlarge(EnlargeTablesByRatio); } protected internal virtual bool EnsureCapacityAndEnlarge(int ratio) { var needToEnlarge = _fieldsIndex.IsNeedToEnlarge(); if (needToEnlarge) { _fieldsIndex.EnlargeIndex(ratio); _fieldsIndex.EnlargeHastable(ratio); _perFieldStorage.Enlarge(ratio); OnEnlarge(ratio, _fieldsIndex.GetIndexCapacity()); } return needToEnlarge; } protected internal virtual void OnEnlarge(int ratio, int newSize) { } protected internal virtual int GetIndexCapacity() { return _fieldsIndex.GetIndexCapacity(); } public int ToByteArrayAndReturnNextPosition(byte[] dst, int offset, int[] excludedFields) { var size = Count; for (var i = 0; i < size; i++) { var tagId = _fieldsIndex.GetTag(i); if (IsExcludeTag(tagId, excludedFields)) { continue; } if (IsUnserializableTag(tagId)) { continue; } // maskedTags is null because all three usages not require masking offset = TagToByteArrayAndReturnNextPosition(dst, offset, i, null); dst[offset++] = SOH; } return offset; } /*protected*/ internal virtual int GenericMessageToByteArrayAndReturnNextPosition(byte[] dst, int offset, IMaskedTags maskedTags) { //TBD! use iterator var size = Count; for (var i = 0; i < size; i++) { offset = TagToByteArrayAndReturnNextPosition(dst, offset, i, maskedTags); dst[offset++] = SOH; } return offset; } /*protected*/ internal virtual int PreparedToByteArrayAndReturnNextPosition(byte[] dst, int offset, IMaskedTags maskedTags) { //TBD! add processing of special case: whole message original buffer was copied to arena - no needs to serialize by field-by-field //init with offset of the first tag (hope it will be in original buffer) var startIndex = 0; var size = Count; while (startIndex < size && !_fieldsIndex.IsOriginalMessageStorage(startIndex)) { offset = TagToByteArrayAndReturnNextPosition(dst, offset, startIndex, maskedTags); dst[offset++] = SOH; startIndex++; } if (startIndex == size) { //in previous loop was processed all fields return offset; } var stripBroken = false; var tagId = _fieldsIndex.GetTag(startIndex); var stripStart = _fieldsIndex.GetOffset(startIndex) - 1 - GetTagBytesLength(tagId); for (var i = startIndex; i < size; i++) { if (_fieldsIndex.IsOriginalMessageStorage(i)) { // TBD! throw an exception, write to log ? //System.out.println("Prepared message continuity broken at tag: " + tagId); //int stretchLen = 0; if (stripBroken) { tagId = _fieldsIndex.GetTag(i); stripBroken = false; stripStart = _fieldsIndex.GetOffset(i) - 1 - GetTagBytesLength(tagId); } } else { if (!stripBroken) { stripBroken = true; var lastOrigTagOffset = _fieldsIndex.GetOffset(i - 1); var lastOrigTagLength = _fieldsIndex.GetLength(i - 1); var stripLen = lastOrigTagOffset + lastOrigTagLength + 1 - stripStart; Array.Copy(GetOrigBuffer().Buffer, stripStart, dst, offset, stripLen); offset += stripLen; } // current tag is out of orig buffer and should be processed separately offset = TagToByteArrayAndReturnNextPosition(dst, offset, i, maskedTags); dst[offset++] = SOH; } } if (!stripBroken) { var lastTagId = size - 1; var lastTagOffset = _fieldsIndex.GetOffset(lastTagId); var lastTagLength = _fieldsIndex.GetLength(lastTagId); var stretchLen = lastTagOffset + lastTagLength + 1 - stripStart; var byteArray = GetOrigBuffer().Buffer; Array.Copy(byteArray, stripStart, dst, offset, stretchLen); offset += stretchLen; } return offset; } internal void GetMessageBuffer(MsgBuf buf) { var stg = GetOrigBuffer(); buf.Buffer = stg.Buffer; buf.Offset = stg.Offset; buf.Length = stg.Length; } protected internal virtual void SetOriginalBuffer(byte[] buf, int offset, int length) { _origBuffer.SetBuffer(buf, offset, length); } internal virtual void ShiftBuffer(byte[] buf, int offset, int length) { var shift = offset - _origBuffer.Offset; _fieldsIndex.ShiftBuffer(shift); _origBuffer.SetBuffer(buf, offset, length); } protected internal virtual void TransferDataToArena() { if (_origBuffer.IsActive) { var newOrigBufStartOffset = _arenaStorage.GetOffset(); var origByteArray = _origBuffer.Buffer; var startOffset = _origBuffer.Offset; _arenaStorage.Add(origByteArray, startOffset, _origBuffer.Length); _origBuffer.ClearAll(); _fieldsIndex.ShiftBufferAndChangeStorage(newOrigBufStartOffset - startOffset, FieldIndex.FlagArenaStorage); } } protected internal virtual int MapTagInOrigStorage(int tag, int offset, int length) { EnsureCapacityAndEnlarge(); return _fieldsIndex.Add(tag, offset, length, FieldIndex.FlagOrigbufStorage); } protected internal virtual int MapPreparedTagInOrigStorage(int tag, int offset, int length) { EnsureCapacityAndEnlarge(); return _fieldsIndex.Add(tag, offset, length, FieldIndex.FlagOrigbufStorage | FieldIndex.FlagPreparedTag); } internal virtual void InitRepeatingGroupStorage(FixVersionContainer version, string msgType, bool validation) { if (_repeatingGroupStorage == null) { _repeatingGroupStorage = new RepeatingGroupStorage(this, version, msgType, validation); } else if (_repeatingGroupStorage.IsInvalidated) { _repeatingGroupStorage.Init(version, msgType, validation); } else { _repeatingGroupStorage.ClearRepeatingGroupStorage(); _repeatingGroupStorage.Init(version, msgType, validation); } } internal virtual void StartCreateRg(int leadingTag, int delimTag, int leadingTagIndex) { var size = (int)GetTagValueAsLongAtIndex(leadingTagIndex); _repeatingGroupStorage.StartCreateRg(leadingTag, leadingTagIndex, size, delimTag); } internal virtual void StopCreateRg() { _repeatingGroupStorage.StopCreateRg(); } internal virtual void AddTagToRg(int tag, int tagIndex, int counterTag) { _repeatingGroupStorage.AddTag(tag, tagIndex, counterTag); } /// <summary> /// Fills passed repeating group instance by data from storage. If group doesn't exist, throws exception. </summary> /// <param name="leadingTag"> leading tag for repeating group </param> /// <param name="group"> repeating group object for fill </param> public virtual void GetRepeatingGroup(int leadingTag, RepeatingGroup group) { if (_repeatingGroupStorage == null || _repeatingGroupStorage.IsInvalidated) { InitRepeatingGroupStorage(false); } _repeatingGroupStorage.GetRepeatingGroup(leadingTag, group); } /// <summary> /// Returns repeating group from storage by leading tag. If group doesn't exist, returns null. </summary> /// <param name="leadingTag"> leading tag for repeating group </param> /// <returns> instance of RepeatingGroup from RepeatingGroupPool. There is no need to call release for this object. </returns> public virtual RepeatingGroup GetRepeatingGroup(int leadingTag) { if (_repeatingGroupStorage == null || _repeatingGroupStorage.IsInvalidated) { InitRepeatingGroupStorage(false); } return _repeatingGroupStorage.GetRepeatingGroup(leadingTag); } /// <summary> /// Returns repeating group from storage by leading tag. If group doesn't exist, add group at end of message. </summary> /// <param name="leadingTag"> leading tag for repeating group </param> /// <returns> instance of RepeatingGroup from RepeatingGroupPool. There is no need to call release for this object. </returns> public virtual RepeatingGroup GetOrAddRepeatingGroup(int leadingTag) { if (IsRepeatingGroupExists(leadingTag)) { return GetRepeatingGroup(leadingTag); } return AddRepeatingGroup(leadingTag); } /// <summary> /// Fills passed repeating group instance by data from storage. If group doesn't exist, add group at end of of message </summary> /// <param name="leadingTag"> leading tag for repeating group </param> /// <param name="group"> repeating group object for fill </param> public virtual void GetOrAddRepeatingGroup(int leadingTag, RepeatingGroup group) { if (IsRepeatingGroupExists(leadingTag)) { GetRepeatingGroup(leadingTag, group); } else { AddRepeatingGroup(leadingTag, group); } } /// <summary> /// Returns repeating group from storage by leading tag. If group doesn't exist, add group at passed index in message. </summary> /// <param name="leadingTag"> leading tag for repeating group </param> /// <returns> instance of RepeatingGroup from RepeatingGroupPool. There is no need to call release for this object. </returns> public virtual RepeatingGroup GetOrAddRepeatingGroupAtIndex(int leadingTag, int index) { if (IsRepeatingGroupExists(leadingTag)) { return GetRepeatingGroup(leadingTag); } return AddRepeatingGroupAtIndex(index, leadingTag); } /// <summary> /// Returns repeating group from storage by leading tag. If group doesn't exist, add group at passed index in message. </summary> /// <param name="leadingTag"> leading tag for repeating group </param> /// <returns> instance of RepeatingGroup from RepeatingGroupPool. There is no need to call release for this object. </returns> public virtual void GetOrAddRepeatingGroupAtIndex(int leadingTag, int index, RepeatingGroup group) { if (IsRepeatingGroupExists(leadingTag)) { GetRepeatingGroup(leadingTag, group); } else { AddRepeatingGroupAtIndex(index, leadingTag, group); } } /// <summary> /// Returns repeating group, founded by index of leading tag </summary> /// <param name="index"> index of repeating group's leading tag at message </param> /// <returns> instance of RepeatingGroup from RepeatingGroupPool. There is no need to call release for this object. </returns> public virtual RepeatingGroup GetRepeatingGroupAtIndex(int index) { if (_repeatingGroupStorage == null || _repeatingGroupStorage.IsInvalidated) { InitRepeatingGroupStorage(false); } var leadingTag = GetTagIdAtIndex(index); return GetRepeatingGroup(leadingTag); } /// <summary> /// Fills passed repeating group instance by data, founded by index of leading tag </summary> /// <param name="index"> leading tag for repeating group </param> /// <param name="group"> repeating group object for fill </param> public virtual void GetRepeatingGroupAtIndex(int index, RepeatingGroup group) { var leadingTag = GetTagIdAtIndex(index); GetRepeatingGroup(leadingTag, group); } /// <summary> /// Checks is message contains not nested group with passed leading tag. /// Note that empty groups, that doesn't appear in the message, also considered existing </summary> /// <param name="leadingTag"> leading tag for check </param> /// <returns> true if repeating group exists. </returns> public virtual bool IsRepeatingGroupExists(int leadingTag) { if (_repeatingGroupStorage == null || _repeatingGroupStorage.IsInvalidated) { InitRepeatingGroupStorage(false); } return _repeatingGroupStorage.IsRepeatingGroupExists(leadingTag); } /// <summary> /// Removes repeating group with specified leading tag </summary> /// <param name="leadingTag"> leading tag of group </param> /// <returns> true if there is group with specified leading tag </returns> public virtual bool RemoveRepeatingGroup(int leadingTag) { if (IsRepeatingGroupExists(leadingTag)) { GetRepeatingGroup(leadingTag).Remove(); return true; } return false; } /// <summary> /// Removes repeating group with leading tag at specified index </summary> /// <param name="index"> of repeating group's leading tag </param> /// <returns> true if there is group with specified leading tag </returns> public virtual bool RemoveRepeatingGroupAtIndex(int index) { return RemoveRepeatingGroup(GetTagIdAtIndex(index)); } private void InitRepeatingGroupStorage(bool validation) { if (_repeatingGroupStorage == null || _repeatingGroupStorage.IsInvalidated) { RawFixUtil.IndexRepeatingGroup(this, validation); } } /// <summary> /// Returns all inner arrays, RepeatingGroup and Entry back to pool. /// It returns only those RepeatingGroup and Entry, that have been got implicitly from addRepeatingGroup/getRepeatingGroup. /// If you got RepeatingGroup or Entry explicit from RepeatingGroupPool, you should take care of call release. /// Also this method implicitly calls in <see cref="FixMessage.ReleaseInstance"/>. /// </summary> public virtual void InvalidateRepeatingGroupIndex() { if (_repeatingGroupStorage != null) { _repeatingGroupStorage.ClearRepeatingGroupStorage(); } } /// <summary> /// Adds group without validation at the end of message. Trailer is ignored. </summary> /// <param name="leadingTag"> leading tag for new group </param> /// <param name="group"> repeating group object for further work with group </param> public virtual void AddRepeatingGroup(int leadingTag, RepeatingGroup group) { AddRepeatingGroupAtIndex(Count, leadingTag, group); } /// <summary> /// Adds group without validation at the end of message. Trailer is ignored. /// Repeating </summary> /// <param name="leadingTag"> leading tag for new group </param> /// <returns> instance of RepeatingGroup from RepeatingGroupPool. There is no need to call release for this object. </returns> public virtual RepeatingGroup AddRepeatingGroup(int leadingTag) { return AddRepeatingGroupAtIndex(Count, leadingTag); } /// <summary> /// Adds group at the end of message. Trailer is ignored. </summary> /// <param name="leadingTag"> leading tag for new group </param> /// <param name="validation"> turn on/off validation </param> /// <returns> instance of RepeatingGroup from RepeatingGroupPool. There is no need to call release for this object. </returns> public virtual RepeatingGroup AddRepeatingGroup(int leadingTag, bool validation) { return AddRepeatingGroupAtIndex(Count, leadingTag, validation); } /// <summary> /// Adds group without validation at the end of message. Trailer is ignored. </summary> /// <param name="leadingTag"> leading tag for new group </param> /// <param name="group"> repeating group object for further work with group </param> public virtual void AddRepeatingGroup(int leadingTag, bool validation, RepeatingGroup group) { AddRepeatingGroupAtIndex(Count, leadingTag, validation, group); } /// <summary> /// Adds group at specific place at message </summary> /// <param name="index"> index in FIX message. Leading tag will be inserted at this index and all other group tags will follow. </param> /// <param name="leadingTag"> leading tag for new group </param> /// <param name="validation"> turn on/off validation </param> /// <param name="group"> repeating group object for further work with group </param> public virtual void AddRepeatingGroupAtIndex(int index, int leadingTag, bool validation, RepeatingGroup group) { if (_repeatingGroupStorage == null || _repeatingGroupStorage.IsInvalidated) { InitRepeatingGroupStorage(validation); } if (validation) { _repeatingGroupStorage.ValidateLeadingTag(leadingTag); } _repeatingGroupStorage.ValidateGroupDuplicate(leadingTag); _repeatingGroupStorage.AddRepeatingGroup(index, leadingTag, validation, group); } /// <summary> /// Adds group at specific place at message </summary> /// <param name="index"> index in FIX message. Leading tag will be inserted at this index and all other group tags will follow. </param> /// <param name="leadingTag"> leading tag for new group </param> /// <param name="validation"> turn on/off validation </param> /// <returns> instance of RepeatingGroup from RepeatingGroupPool. There is no need to call release for this object. </returns> public virtual RepeatingGroup AddRepeatingGroupAtIndex(int index, int leadingTag, bool validation) { if (_repeatingGroupStorage == null || _repeatingGroupStorage.IsInvalidated) { InitRepeatingGroupStorage(validation); } var group = RepeatingGroupPool.RepeatingGroup; AddRepeatingGroupAtIndex(index, leadingTag, validation, group); return group; } /// <summary> /// Adds group at specific place at message </summary> /// <param name="index"> index in FIX message. Leading tag will be inserted at this index and all other group tags will follow. </param> /// <param name="leadingTag"> leading tag for new group </param> /// <returns> instance of RepeatingGroup from RepeatingGroupPool. There is no need to call release for this object. </returns> public virtual RepeatingGroup AddRepeatingGroupAtIndex(int index, int leadingTag) { var group = RepeatingGroupPool.RepeatingGroup; AddRepeatingGroupAtIndex(index, leadingTag, false, group); return group; } /// <summary> /// Adds group at specific place at message </summary> /// <param name="index"> index in FIX message. Leading tag will be inserted at this index and all other group tags will follow. </param> /// <param name="leadingTag"> leading tag for new group </param> /// <param name="group"> repeating group object for further work with group </param> public virtual void AddRepeatingGroupAtIndex(int index, int leadingTag, RepeatingGroup group) { AddRepeatingGroupAtIndex(index, leadingTag, false, group); } /// <summary> /// Copy repeating group at end of message </summary> /// <param name="source"> repeating group for copy </param> /// <returns> copied repeating group </returns> public virtual RepeatingGroup CopyRepeatingGroup(RepeatingGroup source) { return CopyRepeatingGroup(source, Count); } /// <summary> /// Copy repeating group at specified index of message </summary> /// <param name="source"> repeating group for copy </param> /// <param name="index"> index at which the source entry is to be copied </param> /// <returns> copied repeating group </returns> public virtual RepeatingGroup CopyRepeatingGroup(RepeatingGroup source, int index) { var dest = AddRepeatingGroupAtIndex(index, source.LeadingTag); for (var i = 0; i < source.Count; i++) { dest.CopyEntry(source.GetEntry(i)); } return dest; } /// <summary> /// Copy repeating group at end of message </summary> /// <param name="source"> repeating group for copy </param> /// <param name="dest"> entry for hold copied repeating group </param> public virtual void CopyRepeatingGroup(RepeatingGroup source, RepeatingGroup dest) { CopyRepeatingGroup(source, dest, Count); } /// <summary> /// Copy repeating group at specified index of message </summary> /// <param name="source"> repeating group for copy </param> /// <param name="dest"> entry for hold copied repeating group </param> /// <param name="index"> index at which the source entry is to be copied </param> public virtual void CopyRepeatingGroup(RepeatingGroup source, RepeatingGroup dest, int index) { AddRepeatingGroupAtIndex(index, source.LeadingTag, source.Validation, dest); for (var i = 0; i < source.Count; i++) { dest.CopyEntry(source.GetEntry(i)); } } public override int GetHashCode() { return _fieldsIndex.GetHashCode(); } /// <param name="tag"> </param> /// <param name="addIfNotExists"> </param> /// <returns> index of tag </returns> protected internal virtual int FindOrPrepareToAdd(int tag, MissingTagHandling addIfNotExists) { return FindOrPrepareToAdd(tag, 1, addIfNotExists); } protected internal virtual int FindOrPrepareToAdd(int tag, int occurrence, MissingTagHandling addIfNotExists) { var index = FieldIndex.Notfound; if (addIfNotExists != MissingTagHandling.AlwaysAdd) { index = GetTagIndex(tag, occurrence); } if (addIfNotExists == MissingTagHandling.AlwaysAdd || index == FieldIndex.Notfound && addIfNotExists == MissingTagHandling.AddIfNotExists) { EnsureCapacityAndEnlarge(); index = _fieldsIndex.Add(tag, 0, -1, 0); } return index; } private MessageStorage NewStorageForEntry(int tagIndex, int length) { _continuousBuffer = false; if (!_arenaStorage.Overflow()) { //put in arena storage _fieldsIndex.UpdateStorageData(tagIndex, FieldIndex.FlagArenaStorage, _arenaStorage.GetOffset(), length); return _arenaStorage; } //put in perFieldStorage _fieldsIndex.UpdateStorageData(tagIndex, FieldIndex.FlagPerfieldStorage, 0, length); _perFieldStorage.Init(tagIndex); return _perFieldStorage; } protected internal virtual bool CanCopyInPlaceNumber(int index, int oldLen, int length) { if (_fieldsIndex.GetStorageType(index) == 0) { //storage not defined return false; } if (_fieldsIndex.IsOriginalMessageStorage(index)) { if (oldLen >= length) { return true; } // debug // TBD! throw an exception, write to log ? //System.out.println("Prepared message continuity will break due to tag assignment: tag " + fieldsIndex.getTag(index) + ", expected value length " + oldLen + ", assigned length " + length); //log.warn("Prepared message continuity will break due to tag assignment: tag " + fieldsIndex.getTag(index) + ", expected value length " + oldLen + ", assigned length " + length); return false; } if (oldLen >= length) { return true; } var maxAvail = _fieldsIndex.GetMaxAvailableInPlace(index); return maxAvail >= length; } protected internal virtual bool CanCopyInPlace(int index, int oldLen, int length) { if (_fieldsIndex.GetStorageType(index) == 0) { //storage not defined return false; } if (_fieldsIndex.IsOriginalMessageStorage(index)) { if (_fieldsIndex.IsPreparedOriginalStorage(index)) { if (oldLen >= length) { return true; } return false; } if (oldLen == length) { return true; } // debug // TBD! throw an exception, write to log ? //System.out.println("Prepared message continuity will break due to tag assignment: tag " + fieldsIndex.getTag(index) + ", expected value length " + oldLen + ", assigned length " + length); //log.warn("Prepared message continuity will break due to tag assignment: tag " + fieldsIndex.getTag(index) + ", expected value length " + oldLen + ", assigned length " + length); return false; } return false; } protected internal virtual bool CanCopyInPlaceWithLengthReducing(int index, int oldLen, int length) { var storageType = _fieldsIndex.GetStorageType(index); if (storageType != FieldIndex.FlagArenaStorage && storageType != FieldIndex.FlagPerfieldStorage) { //this is possible only for ByteBuffer based storages return false; } return oldLen >= length; } /*protected*/ internal virtual MessageStorage GetStorage(int index) { var storageType = GetStorageType(index); switch (storageType) { case FieldIndex.FlagOrigbufStorage: return _origBuffer.IsActive ? (MessageStorage)_origBuffer : _arenaStorage; case FieldIndex.FlagArenaStorage: return _arenaStorage; case FieldIndex.FlagPerfieldStorage: return _perFieldStorage; } throw new Exception("internal error: field storage not set"); } protected internal virtual int GetStorageType(int index) { _fieldsIndex.CheckTagExistsAtIndex(index); return _fieldsIndex.GetStorageType(index); } private void ReloadTagValue(int index, TagValue tagValue) { var buffer = GetStorage(index).GetByteArray(index); var offset = _fieldsIndex.GetOffset(index); var length = _fieldsIndex.GetLength(index); var tagId = _fieldsIndex.GetTag(index); tagValue.Reload(tagId, buffer, offset, length); } private void RemoveElementFromIndex(int index, bool shiftRg) { InvalidatePerFieldStorage(index); var fieldCount = _fieldsIndex.Count; _fieldsIndex.RemoveElementFromIndex(index); if (index < fieldCount - 1) { _perFieldStorage.ShiftBack(index, 1, fieldCount); } if (shiftRg && _repeatingGroupStorage != null && !_repeatingGroupStorage.IsInvalidated) { _repeatingGroupStorage.Shift(index, -1, -1, -1, true); } } private void InvalidatePerFieldStorage(int tagIndex) { _perFieldStorage.Clear(tagIndex); } private IContinuousMessageStorage GetOrigBuffer() { return _origBuffer.IsActive ? (IContinuousMessageStorage)_origBuffer : _arenaStorage; } private int TagToByteArrayAndReturnNextPosition(byte[] dst, int offset, int tagIndex, IMaskedTags maskedTags) { var tag = _fieldsIndex.GetTag(tagIndex); var maskTag = maskedTags != null && maskedTags.IsTagListed(tag); var tagBytesLength = GetTagBytesLength(tag); var equalsPosition = offset += tagBytesLength; do { dst[--offset] = (byte)(tag % 10 + '0'); } while ((tag /= 10) > 0); dst[equalsPosition] = (byte)'='; var len = _fieldsIndex.GetLength(tagIndex); if (len != 0) { if (maskTag) { dst.Fill((byte)'*', equalsPosition + 1, len); } else { //copy only if there is value Array.Copy(GetStorage(tagIndex).GetByteArray(tagIndex), _fieldsIndex.GetOffset(tagIndex), dst, equalsPosition + 1, len); } } return equalsPosition + len + 1; } private int GetTagBytesLength(int tag) { var length = 1; while ((tag /= 10) > 0) { length++; } return length; } private bool IsExcludeTag(int tag, int[] tags) { if (tags == null) { return false; } foreach (var excludeTag in tags) { if (excludeTag == tag) { return true; } } return false; } internal virtual ByteArrayMessageStorage GetOriginalStorage() { return _origBuffer; } internal virtual ArenaMessageStorage GetArenaStorage() { return _arenaStorage; } internal virtual PerFieldMessageStorage GetPerFieldStorage() { return _perFieldStorage; } public virtual FixVersionContainer GetFixVersion() { if (_fixVersion != null) { return _fixVersion; } var baseVersionIndex = GetTagIndex(Tags.BeginString); if (baseVersionIndex != NotFound) { var baseFixVersion = FixVersion.GetInstanceByMessageVersion(GetTagValueAsStringAtIndex(baseVersionIndex)); if (baseFixVersion == FixVersion.Fixt11) { var appVersionIndex = GetTagIndex(Tags.ApplVerID); if (appVersionIndex != NotFound) { baseFixVersion = FixVersion.GetInstanceByFixtVersion((int)GetTagValueAsLongAtIndex(appVersionIndex)); } } return FixVersionContainer.GetFixVersionContainer(baseFixVersion); } return null; } internal virtual void SetFixVersion(FixVersionContainer version) { _fixVersion = version; } public void MarkUnserializableTag(in int tag) { _unserializableTags.Add(tag); } public void ClearUnserializableTags() { _unserializableTags.Clear(); } private bool IsUnserializableTag(in int tag) { return _unserializableTags.Contains(tag); } } }