/// <summary>
 /// Initializes a new instance of the <see cref="BinaryStructureTracker" /> class.
 /// </summary>
 /// <param name="desc">The desc.</param>
 /// <param name="portStruct">The structure to work with.</param>
 public BinaryStructureTracker(IBinaryTypeDescriptor desc, BinaryStructure portStruct)
 {
     _desc = desc;
     _portStruct = portStruct;
     _curStructPath = 0;
     _curStructAction = 0;
     _curStructUpdates = null;
 }
 /** <inheritDoc /> */
 public void UpdateReadStructure(BinaryStructure exp, int pathIdx, 
     IList<BinaryStructureUpdate> updates)
 {
     lock (this)
     {
         _readerTypeStructure = _readerTypeStructure.Merge(exp, pathIdx, updates);
     }
 }
 /** <inheritDoc /> */
 public void UpdateWriteStructure(BinaryStructure exp, int pathIdx, 
     IList<BinaryStructureUpdate> updates)
 {
     lock (this)
     {
         _writerTypeStruct = _writerTypeStruct.Merge(exp, pathIdx, updates);
     }
 }
Example #4
0
        /// <summary>
        /// Merge updates into a new type structure.
        /// </summary>
        /// <param name="exp">Expected type structure to apply updates to </param>
        /// <param name="pathIdx">Path index.</param>
        /// <param name="updates">Updates.</param>
        /// <returns>New type structure with updates.</returns>
        public BinaryStructure Merge(BinaryStructure exp, int pathIdx, 
            IList<BinaryStructureUpdate> updates)
        {
            if (updates.Count == 0)
                return this;

            // Algorithm ensures that updates are applied to the same type structure,
            // where they were initially observed. This allow us to keep structure
            // internals simpler and more efficient. On the other hand, this imposes
            // some performance hit because in case of concurrent update, recorded
            // changes will be discarded and recorded again during the next write
            // on the same path. This should occur only during application warmup.

            // Note that field types are merged anyway to avoid metadata clashes.
            BinaryStructure res = MergeFieldTypes(updates);

            if (ReferenceEquals(exp, this))
            {
                BinaryStructureUpdate firstUpdate = updates[0];

                if (firstUpdate.Index == 0)
                {
                    // Special case: the very first structure update. Simply attach all updates.
                    Debug.Assert(_paths.Length == 1);
                    Debug.Assert(_paths[0].Length == 0);
                    Debug.Assert(pathIdx == 0);

                    var newPaths = CopyPaths(updates.Count, 0);

                    ApplyUpdatesToPath(newPaths[0], updates);

                    res = new BinaryStructure(newPaths, _jumps, res._fieldTypes);
                }
                else
                {
                    // Get entry where updates should start.
                    BinaryStructureEntry[] path = _paths[pathIdx];

                    BinaryStructureEntry startEntry = default(BinaryStructureEntry);

                    if (firstUpdate.Index < path.Length)
                        startEntry = path[firstUpdate.Index];

                    if (startEntry.IsEmpty)
                    {
                        // We are on the empty/non-existent entry. Continue the path without branching.
                        var newPaths = CopyPaths(firstUpdate.Index + updates.Count, 0);

                        ApplyUpdatesToPath(newPaths[pathIdx], updates);

                        res = new BinaryStructure(newPaths, _jumps, res._fieldTypes);
                    }
                    else if (startEntry.IsJumpTable)
                    {
                        // We are on the jump table. Add a new path and record it in the jump table.

                        // 1. Prepare new structures.
                        var newPaths = CopyPaths(firstUpdate.Index + updates.Count, 1);
                        var newJumps = CopyJumps(0);

                        // New path will be the last one.
                        int newPathIdx = newPaths.Length - 1;

                        // Apply updates to the new path.
                        ApplyUpdatesToPath(newPaths[newPathIdx], updates);

                        // Add the jump to the table.
                        newJumps[startEntry.Id] = 
                            newJumps[startEntry.Id].CopyAndAdd(firstUpdate.FieldName, newPathIdx);

                        res = new BinaryStructure(newPaths, newJumps, res._fieldTypes);
                    }
                    else
                    {
                        // We are on existing entry. Need to create a new jump table here and two new paths.

                        // 1. Prepaare new structures.
                        var newPaths = CopyPaths(firstUpdate.Index + updates.Count, 2);
                        var newJumps = CopyJumps(1);

                        // Old path will be moved here.
                        int oldPathIdx = newPaths.Length - 2;

                        // New path will reside here.
                        int newPathIdx = newPaths.Length - 1;

                        // Create new jump table.
                        int newJumpIdx = newJumps.Length - 1;

                        newJumps[newJumpIdx] = new BinaryStructureJumpTable(startEntry.Name, oldPathIdx,
                            firstUpdate.FieldName, newPathIdx);

                        // Re-create old path in two steps: move old path to the new place, then clean the old path.
                        for (int i = firstUpdate.Index; i < path.Length; i++)
                        {
                            newPaths[oldPathIdx][i] = newPaths[pathIdx][i];

                            if (i == firstUpdate.Index)
                                // Inject jump table ...
                                newPaths[pathIdx][i] = new BinaryStructureEntry(newJumpIdx);
                            else
                                // ... or just reset.
                                newPaths[pathIdx][i] = new BinaryStructureEntry();
                        }

                        // Apply updates to the new path.
                        ApplyUpdatesToPath(newPaths[newPaths.Length - 1], updates);

                        res = new BinaryStructure(newPaths, newJumps, res._fieldTypes);
                    }

                }
            }

            return res;
        }