예제 #1
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="paths">Paths.</param>
 /// <param name="jumps">Jumps.</param>
 /// <param name="fieldTypes">Field types.</param>
 private BinaryStructure(BinaryStructureEntry[][] paths,
     BinaryStructureJumpTable[] jumps, IDictionary<string, byte> fieldTypes)
 {
     _paths = paths;
     _jumps = jumps;
     _fieldTypes = fieldTypes;
 }
예제 #2
0
        /// <summary>
        /// Gets field ID if possible.
        /// </summary>
        /// <param name="fieldName">Field name.</param>
        /// <param name="fieldType">Field type.</param>
        /// <param name="pathIdx">Path index, changes during jumps.</param>
        /// <param name="actionIdx">Action index.</param>
        /// <returns>Field ID or zero in case there are no matching path.</returns>
        public int GetFieldId(string fieldName, byte fieldType, ref int pathIdx, int actionIdx)
        {
            Debug.Assert(fieldName != null);
            Debug.Assert(pathIdx <= _paths.Length);

            // Get path.
            BinaryStructureEntry[] path = _paths[pathIdx];

            if (actionIdx < path.Length)
            {
                // Get entry matching the action index.
                BinaryStructureEntry entry = path[actionIdx];

                if (entry.IsExpected(fieldName, fieldType))
                {
                    // Entry matches our expectations, return.
                    return(entry.Id);
                }

                if (entry.IsJumpTable)
                {
                    // Entry is a pointer to a jump table.
                    Debug.Assert(entry.Id < _jumps.Length);

                    BinaryStructureJumpTable jmpTbl = _jumps[entry.Id];

                    if (jmpTbl == null)
                    {
                        return(0);
                    }

                    int pathIdx0 = jmpTbl.GetPathIndex(fieldName);

                    if (pathIdx0 < 0)
                    {
                        return(0);
                    }

                    Debug.Assert(pathIdx0 < _paths.Length);

                    entry = _paths[pathIdx0][actionIdx];

                    entry.ValidateType(fieldType);

                    pathIdx = pathIdx0;

                    return(entry.Id);
                }
            }

            // Failed to find anything because this is a new field.
            return(0);
        }
예제 #3
0
        /// <summary>
        /// Copy and possibly expand jump tables.
        /// </summary>
        /// <param name="additionalJumps">Amount of additional jumps required.</param>
        /// <returns>Result.</returns>
        private BinaryStructureJumpTable[] CopyJumps(int additionalJumps)
        {
            var newJumps = new BinaryStructureJumpTable[_jumps.Length + additionalJumps];

            // The very first jump is always null so that we can distinguish between jump table
            // and empty value in BinaryStructureEntry.
            for (int i = 1; i < _jumps.Length; i++)
            {
                newJumps[i] = _jumps[i].Copy();
            }

            return(newJumps);
        }
예제 #4
0
        /// <summary>
        /// Merge updates into a new type structure.
        /// </summary>
        /// <param name="pathIdx">Path index.</param>
        /// <param name="updates">Updates.</param>
        /// <returns>New type structure with updates.</returns>
        public BinaryStructure Merge(int pathIdx, IList <BinaryStructureUpdate> updates)
        {
            if (updates == null || 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);
            BinaryStructureUpdate firstUpdate = updates[0];

            // Get entry where updates should start.
            BinaryStructureEntry[] path = _paths[pathIdx];

            var startEntry = firstUpdate.Index < path.Length
                ? path[firstUpdate.Index]
                : default(BinaryStructureEntry);

            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. Prepare 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);
        }
예제 #5
0
        /// <summary>
        /// Copy and possibly expand jump tables.
        /// </summary>
        /// <param name="additionalJumps">Amount of additional jumps required.</param>
        /// <returns>Result.</returns>
        private BinaryStructureJumpTable[] CopyJumps(int additionalJumps)
        {
            var newJumps = new BinaryStructureJumpTable[_jumps.Length + additionalJumps];

            // The very first jump is always null so that we can distinguish between jump table
            // and empty value in BinaryStructureEntry.
            for (int i = 1; i < _jumps.Length; i++)
                newJumps[i] = _jumps[i].Copy();

            return newJumps;
        }
예제 #6
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;
        }