/// <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); } }
/// <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; }