IGNITE-1282: Refactoring.

Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/b34084eb
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/b34084eb
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/b34084eb

Branch: refs/heads/ignite-1282-opto
Commit: b34084eb053f4f2bd249bf5a51f487988e3030ac
Parents: 11f7d09
Author: vozerov-gridgain <voze...@gridgain.com>
Authored: Wed Oct 7 11:44:24 2015 +0300
Committer: vozerov-gridgain <voze...@gridgain.com>
Committed: Wed Oct 7 11:44:24 2015 +0300

----------------------------------------------------------------------
 .../Apache.Ignite.Core.csproj                   |   8 +-
 .../Impl/Portable/IPortableTypeDescriptor.cs    |   6 +-
 .../Metadata/Opto/PortableTypeStructure.cs      | 317 -------------------
 .../Metadata/Opto/PortableTypeStructureEntry.cs | 127 --------
 .../Opto/PortableTypeStructureJumpTable.cs      | 115 -------
 .../Opto/PortableTypeStructureUpdate.cs         |  84 -----
 .../Impl/Portable/PortableFullTypeDescriptor.cs |  10 +-
 .../Portable/PortableSurrogateTypeDescriptor.cs |  10 +-
 .../Impl/Portable/PortableWriterImpl.cs         |  70 ++--
 .../Portable/Structure/PortableStructure.cs     | 317 +++++++++++++++++++
 .../Structure/PortableStructureEntry.cs         | 128 ++++++++
 .../Structure/PortableStructureJumpTable.cs     | 115 +++++++
 .../Structure/PortableStructureUpdate.cs        |  84 +++++
 13 files changed, 701 insertions(+), 690 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj 
b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
index d412181..e8ec7ed 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -250,10 +250,6 @@
     <Compile Include="Impl\Portable\IPortableTypeDescriptor.cs" />
     <Compile Include="Impl\Portable\IPortableWriteAware.cs" />
     <Compile Include="Impl\Portable\Metadata\IPortableMetadataHandler.cs" />
-    <Compile 
Include="Impl\Portable\Metadata\Opto\PortableTypeStructureEntry.cs" />
-    <Compile Include="Impl\Portable\Metadata\Opto\PortableTypeStructure.cs" />
-    <Compile 
Include="Impl\Portable\Metadata\Opto\PortableTypeStructureJumpTable.cs" />
-    <Compile 
Include="Impl\Portable\Metadata\Opto\PortableTypeStructureUpdate.cs" />
     <Compile 
Include="Impl\Portable\Metadata\PortableHashsetMetadataHandler.cs" />
     <Compile Include="Impl\Portable\Metadata\PortableMetadataHolder.cs" />
     <Compile Include="Impl\Portable\Metadata\PortableMetadataImpl.cs" />
@@ -279,6 +275,10 @@
     <Compile Include="Impl\Portable\PortableUtils.cs" />
     <Compile Include="Impl\Portable\PortableWriterImpl.cs" />
     <Compile Include="Impl\Portable\SerializableObjectHolder.cs" />
+    <Compile Include="Impl\Portable\Structure\PortableStructure.cs" />
+    <Compile Include="Impl\Portable\Structure\PortableStructureEntry.cs" />
+    <Compile Include="Impl\Portable\Structure\PortableStructureJumpTable.cs" />
+    <Compile Include="Impl\Portable\Structure\PortableStructureUpdate.cs" />
     <Compile Include="Impl\Portable\TypeResolver.cs" />
     <Compile Include="Impl\Resource\IResourceInjector.cs" />
     <Compile Include="Impl\Resource\ResourceFieldInjector.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
index 389238c..8a84daf 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
@@ -19,7 +19,7 @@ namespace Apache.Ignite.Core.Impl.Portable
 {
     using System;
     using System.Collections.Generic;
-    using Apache.Ignite.Core.Impl.Portable.Metadata.Opto;
+    using Apache.Ignite.Core.Impl.Portable.Structure;
     using Apache.Ignite.Core.Portable;
 
     /// <summary>
@@ -110,7 +110,7 @@ namespace Apache.Ignite.Core.Impl.Portable
         /// <summary>
         /// Type structure.
         /// </summary>
-        PortableTypeStructure TypeStructure { get; }
+        PortableStructure TypeStructure { get; }
 
         /// <summary>
         /// Update type structure.
@@ -118,6 +118,6 @@ namespace Apache.Ignite.Core.Impl.Portable
         /// <param name="exp">Expected type structure.</param>
         /// <param name="pathIdx">Path index.</param>
         /// <param name="updates">Recorded updates.</param>
-        void UpdateStrcuture(PortableTypeStructure exp, int pathIdx, 
IList<PortableTypeStructureUpdate> updates);
+        void UpdateStrcuture(PortableStructure exp, int pathIdx, 
IList<PortableStructureUpdate> updates);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructure.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructure.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructure.cs
deleted file mode 100644
index cdc0859..0000000
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructure.cs
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-namespace Apache.Ignite.Core.Impl.Portable.Metadata.Opto
-{
-    using System;
-    using System.Collections.Generic;
-    using System.Diagnostics;
-    
-    using Apache.Ignite.Core.Portable;
-
-    /// <summary>
-    /// Portable type structure. Cache field IDs and metadata to improve 
marshalling performance.
-    /// Every object write contains a set of field writes. Every unique 
ordered set of written fields
-    /// produce write "path". We cache these paths allowing for very fast 
traverse over object structure
-    /// without expensive map lookups and field ID calculations. 
-    /// </summary>
-    internal class PortableTypeStructure
-    {
-        /// <summary>
-        /// Create empty type structure.
-        /// </summary>
-        /// <returns>Empty type structure.</returns>
-        public static PortableTypeStructure CreateEmpty()
-        {
-            return new PortableTypeStructure(new[] { new 
PortableTypeStructureEntry[0] }, 
-                new PortableTypeStructureJumpTable[1], new Dictionary<string, 
byte>());
-        }
-
-        /** Entries. */
-        private readonly PortableTypeStructureEntry[][] _paths;
-
-        /** Jumps. */
-        private readonly PortableTypeStructureJumpTable[] _jumps;
-
-        /** Field types. */
-        private readonly IDictionary<string, byte> _fieldTypes; 
-
-        /// <summary>
-        /// Constructor.
-        /// </summary>
-        /// <param name="paths">Paths.</param>
-        /// <param name="jumps">Jumps.</param>
-        /// <param name="fieldTypes">Field types.</param>
-        private PortableTypeStructure(PortableTypeStructureEntry[][] paths,
-            PortableTypeStructureJumpTable[] jumps, IDictionary<string, byte> 
fieldTypes)
-        {
-            _paths = paths;
-            _jumps = jumps;
-            _fieldTypes = fieldTypes;
-        }
-
-        /// <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(pathIdx <= _paths.Length);
-
-            // Get path.
-            PortableTypeStructureEntry[] path = _paths[pathIdx];
-
-            if (actionIdx < path.Length)
-            {
-                // Get entry matching the action index.
-                PortableTypeStructureEntry entry = path[actionIdx];
-
-                if (entry.IsExpected(fieldName, fieldType))
-                    // Entry matches our expectations, return.
-                    return entry.Id;
-                else if (entry.IsJumpTable)
-                {
-                    // Entry is a pointer to a jump table.
-                    Debug.Assert(entry.Id < _jumps.Length);
-
-                    PortableTypeStructureJumpTable jmpTbl = _jumps[entry.Id];
-
-                    int pathIdx0 = jmpTbl.GetPathIndex(fieldName);
-
-                    if (pathIdx0 < 0)
-                        return 0;
-
-                    Debug.Assert(pathIdx < _paths.Length);
-
-                    entry = _paths[pathIdx][actionIdx];
-
-                    entry.ValidateType(fieldType);
-
-                    pathIdx = pathIdx0;
-
-                    return entry.Id;
-                }
-            }
-
-            // Failed to find anything because this is a new field.
-            return 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 PortableTypeStructure Merge(PortableTypeStructure exp, int 
pathIdx, 
-            IList<PortableTypeStructureUpdate> 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.
-            PortableTypeStructure res = MergeFieldTypes(updates);
-
-            if (ReferenceEquals(exp, this))
-            {
-                PortableTypeStructureUpdate 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);
-
-                    PortableTypeStructureEntry[][] newPaths = 
CopyPaths(updates.Count, 0);
-
-                    ApplyUpdatesToPath(newPaths[0], updates);
-
-                    res = new PortableTypeStructure(newPaths, _jumps, 
res._fieldTypes);
-                }
-                else
-                {
-                    // Get entry where updates should start.
-                    PortableTypeStructureEntry[] path = _paths[pathIdx];
-
-                    PortableTypeStructureEntry startEntry = 
default(PortableTypeStructureEntry);
-
-                    if (firstUpdate.Index < path.Length)
-                        startEntry = path[firstUpdate.Index];
-
-                    if (startEntry.IsEmpty)
-                    {
-                        // We are on the empty/non-existend entry. Continue 
the path without branching.
-                        var newPaths = CopyPaths(firstUpdate.Index + 
updates.Count, 0);
-
-                        ApplyUpdatesToPath(newPaths[pathIdx], updates);
-
-                        res = new PortableTypeStructure(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. Preapare 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 updats to the new path.
-                        ApplyUpdatesToPath(newPaths[newPathIdx], updates);
-
-                        // Add new jump to the table.
-                        newJumps[startEntry.Id] = 
-                            
newJumps[startEntry.Id].CopyAndAdd(firstUpdate.FieldName, newPathIdx);
-
-                        res = new PortableTypeStructure(newPaths, newJumps, 
res._fieldTypes);
-                    }
-                    else
-                    {
-                        // We are on existing entry. Need to create a new jump 
table here and two new paths.
-
-                        // 1. Preapare 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 
PortableTypeStructureJumpTable(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];
-
-                            newPaths[pathIdx][i] = new 
PortableTypeStructureEntry();
-                        }
-
-                        // Apply updats to the new path.
-                        ApplyUpdatesToPath(newPaths[newPaths.Length - 1], 
updates);
-
-                        res = new PortableTypeStructure(newPaths, newJumps, 
res._fieldTypes);
-                    }
-
-                }
-            }
-
-            return res;
-        }
-
-        /// <summary>
-        /// Copy and possible expand paths.
-        /// </summary>
-        /// <param name="minLen">Minimum length.</param>
-        /// <param name="additionalPaths">Amount of additional paths 
required.</param>
-        /// <returns>Result.</returns>
-        private PortableTypeStructureEntry[][] CopyPaths(int minLen, int 
additionalPaths)
-        {
-            var newPaths = new PortableTypeStructureEntry[_paths.Length + 
additionalPaths][];
-
-            int newPathLen = Math.Max(_paths[0].Length, minLen);
-
-            for (int i = 0; i < _paths.Length; i++)
-            {
-                newPaths[i] = new PortableTypeStructureEntry[newPathLen];
-
-                Array.Copy(_paths[i], newPaths[i], _paths[i].Length);
-            }
-
-            return newPaths;
-        }
-
-        /// <summary>
-        /// Copy and possible expand jump tables.
-        /// </summary>
-        /// <param name="additionalJumps">Additional jumps.</param>
-        /// <returns>Result.</returns>
-        private PortableTypeStructureJumpTable[] CopyJumps(int additionalJumps)
-        {
-            var newJumps = new PortableTypeStructureJumpTable[_jumps.Length + 
additionalJumps];
-
-            for (int i = 0; i < _jumps.Length; i++)
-                newJumps[i] = _jumps[i].Copy();
-
-            return newJumps;
-        }
-
-        /// <summary>
-        /// Apply updates to path.
-        /// </summary>
-        /// <param name="path">Path.</param>
-        /// <param name="updates">Updates.</param>
-        private static void 
ApplyUpdatesToPath(IList<PortableTypeStructureEntry> path,
-            IEnumerable<PortableTypeStructureUpdate> updates)
-        {
-            foreach (var u in updates)
-                path[u.Index] = new PortableTypeStructureEntry(u.FieldName, 
u.FieldId, u.FieldType);
-        }
-
-        /// <summary>
-        /// Merge field types.
-        /// </summary>
-        /// <param name="updates">Updates.</param>
-        /// <returns>Type structure with applied updates.</returns>
-        private PortableTypeStructure 
MergeFieldTypes(IList<PortableTypeStructureUpdate> updates)
-        {
-            IDictionary<string, byte> newFieldTypes = new Dictionary<string, 
byte>(_fieldTypes);
-
-            foreach (PortableTypeStructureUpdate update in updates)
-            {
-                byte expType;
-
-                if (_fieldTypes.TryGetValue(update.FieldName, out expType))
-                {
-                    // This is an old field.
-                    if (expType != update.FieldType)
-                    {
-                        throw new PortableException("Field type mismatch 
detected [fieldName=" + update.FieldName +
-                            ", expectedType=" + expType + ", actualType=" + 
update.FieldType + ']');
-                    }
-                }
-                else
-                    // This is a new field.
-                    newFieldTypes[update.FieldName] = update.FieldType;
-            }
-
-            return newFieldTypes.Count == _fieldTypes.Count ? 
-                this : new PortableTypeStructure(_paths, _jumps, 
newFieldTypes);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureEntry.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureEntry.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureEntry.cs
deleted file mode 100644
index e939d56..0000000
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureEntry.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-namespace Apache.Ignite.Core.Impl.Portable.Metadata.Opto
-{
-    using System.Diagnostics;
-    using Apache.Ignite.Core.Portable;
-
-    /// <summary>
-    /// Portable type structure entry. Might be either a normal field or a 
reference to jump table.
-    /// </summary>
-    internal struct PortableTypeStructureEntry
-    {
-        /** Field name. */
-        private readonly string _name;
-
-        /** Field ID. */
-        private readonly int _id;
-
-        /** Field type. */
-        private readonly byte _type;
-        
-        /// <summary>
-        /// Constructor for jump table entry.
-        /// </summary>
-        /// <param name="jumpTblIdx"></param>
-        public PortableTypeStructureEntry(int jumpTblIdx)
-        {
-            Debug.Assert(jumpTblIdx > 0);
-
-            _name = null;
-            _id = jumpTblIdx;
-            _type = 0;
-        }
-
-        /// <summary>
-        /// Constructor for field entry.
-        /// </summary>
-        /// <param name="name">Field name.</param>
-        /// <param name="id">Field ID.</param>
-        /// <param name="type">Field type.</param>
-        public PortableTypeStructureEntry(string name, int id, byte type)
-        {
-            Debug.Assert(name != null);
-
-            _name = name;
-            _id = id;
-            _type = type;
-        }
-
-        /// <summary>
-        /// Check whether current field entry matches passed arguments.
-        /// </summary>
-        /// <param name="name"></param>
-        /// <param name="type"></param>
-        /// <returns></returns>
-        public bool IsExpected(string name, byte type)
-        {
-            if (!ReferenceEquals(_name, name) && !_name.Equals(name))
-                return false;
-
-            ValidateType(type);
-
-            return true;
-        }
-
-        /// <summary>
-        /// Valide field type.
-        /// </summary>
-        /// <param name="type">Expected type.</param>
-        public void ValidateType(byte type)
-        {
-            if (_type != type)
-            {
-                throw new PortableException("Field type mismatch detected 
[fieldName=" + _name +
-                    ", expectedType=" + _type + ", actualType=" + type + ']');
-            }
-        }
-
-        /// <summary>
-        /// Whether this is an empty entry.
-        /// </summary>
-        /// <returns></returns>
-        public bool IsEmpty
-        {
-            get { return _id == 0; }
-        }
-
-        /// <summary>
-        /// Whether this is a jump table.
-        /// </summary>
-        public bool IsJumpTable
-        {
-            get { return _name == null && _id >= 0; }
-        }
-
-        /// <summary>
-        /// Field name.
-        /// </summary>
-        public string Name
-        {
-            get { return _name; }
-        }
-
-        /// <summary>
-        /// Field ID.
-        /// </summary>
-        public int Id
-        {
-            get { return _id; }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureJumpTable.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureJumpTable.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureJumpTable.cs
deleted file mode 100644
index b2409db..0000000
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureJumpTable.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-namespace Apache.Ignite.Core.Impl.Portable.Metadata.Opto
-{
-    using System;
-    using System.Diagnostics;
-
-    /// <summary>
-    /// Jump table.
-    /// </summary>
-    internal class PortableTypeStructureJumpTable
-    {
-        /** Names. */
-        private readonly string[] _names;
-
-        /** Path indexes. */
-        private readonly int[] _pathIdxs;
-
-        /// <summary>
-        /// Create jump table with two entries.
-        /// </summary>
-        /// <param name="firstName">First name.</param>
-        /// <param name="firstPathIdx">First path index.</param>
-        /// <param name="secondName">Second name.</param>
-        /// <param name="secondPathIdx">Second path index.</param>
-        public PortableTypeStructureJumpTable(string firstName, int 
firstPathIdx, 
-            string secondName, int secondPathIdx)
-        {
-            _names = new[] { firstName, secondName };
-            _pathIdxs = new[] { firstPathIdx, secondPathIdx };
-        }
-
-        /// <summary>
-        /// Constructor.
-        /// </summary>
-        /// <param name="names">Field names.</param>
-        /// <param name="pathIdxs">Path indexes.</param>
-        private PortableTypeStructureJumpTable(string[] names, int[] pathIdxs)
-        {
-            Debug.Assert(_names.Length > 1);
-            Debug.Assert(_names.Length == pathIdxs.Length);
-            
-            _names = names;
-            _pathIdxs = pathIdxs;
-        }
-
-        /// <summary>
-        /// Get path index for the given field.
-        /// </summary>
-        /// <param name="fieldName">Field name.</param>
-        /// <returns>Path index.</returns>
-        public int GetPathIndex(string fieldName)
-        {
-            Debug.Assert(fieldName != null);
-            
-            // Optimistically assume that field name is a literal.
-            for (var i = 0; i < _names.Length; i++)
-            {
-                if (ReferenceEquals(fieldName, _names[i]))
-                    return _pathIdxs[i];
-            }
-
-            // Fallback to slow-path with normal string comparison.
-            for (var i = 0; i < _names.Length; i++)
-            {
-                if (fieldName.Equals(_names[i]))
-                    return _pathIdxs[i];
-            }
-
-            // No path found for the field.
-            return -1;
-        }
-
-        /// <summary>
-        /// Copy jump table.
-        /// </summary>
-        /// <returns>New jump table.</returns>
-        public PortableTypeStructureJumpTable Copy()
-        {
-            return new PortableTypeStructureJumpTable(_names, _pathIdxs);
-        }
-
-        /// <summary>
-        /// Copy jump table with additional jump.
-        /// </summary>
-        /// <param name="name">Field name.</param>
-        /// <param name="pathIdx">Path index.</param>
-        /// <returns>New jump table.</returns>
-        public PortableTypeStructureJumpTable CopyAndAdd(string name, int 
pathIdx)
-        {
-            var newNames = new string[_names.Length + 1];
-            var pathIdxs = new int[_pathIdxs.Length + 1];
-
-            Array.Copy(_names, newNames, _names.Length);
-            Array.Copy(_pathIdxs, pathIdxs, _pathIdxs.Length);
-
-            return new PortableTypeStructureJumpTable(newNames, pathIdxs);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureUpdate.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureUpdate.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureUpdate.cs
deleted file mode 100644
index 56e0c12..0000000
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureUpdate.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-namespace Apache.Ignite.Core.Impl.Portable.Metadata.Opto
-{
-    /// <summary>
-    /// Portable type structure update descriptor.
-    /// </summary>
-    class PortableTypeStructureUpdate
-    {
-        /** Field name. */
-        private readonly string _fieldName;
-
-        /** Field ID. */
-        private readonly int _fieldId;
-
-        /** Field type. */
-        private readonly byte _fieldType;
-
-        /** Field index. */
-        private readonly int _idx;
-
-        /// <summary>
-        /// Constructor.
-        /// </summary>
-        /// <param name="fieldName">Field name.</param>
-        /// <param name="fieldId">Field ID.</param>
-        /// <param name="fieldType">Field type.</param>
-        /// <param name="idx">Index.</param>
-        public PortableTypeStructureUpdate(string fieldName, int fieldId, byte 
fieldType, int idx)
-        {
-            _fieldName = fieldName;
-            _fieldId = fieldId;
-            _fieldType = fieldType;
-            _idx = idx;
-        }
-
-        /// <summary>
-        /// Field name.
-        /// </summary>
-        public string FieldName
-        {
-            get { return _fieldName; }
-        }
-
-        /// <summary>
-        /// Field ID.
-        /// </summary>
-        public int FieldId
-        {
-            get { return _fieldId; }
-        }
-
-        /// <summary>
-        /// Field type.
-        /// </summary>
-        public byte FieldType
-        {
-            get { return _fieldType; }
-        }
-
-        /// <summary>
-        /// Index.
-        /// </summary>
-        public int Index
-        {
-            get { return _idx; }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
index d1a714b..247a0b0 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
@@ -19,7 +19,7 @@ namespace Apache.Ignite.Core.Impl.Portable
 {
     using System;
     using System.Collections.Generic;
-    using Apache.Ignite.Core.Impl.Portable.Metadata.Opto;
+    using Apache.Ignite.Core.Impl.Portable.Structure;
     using Apache.Ignite.Core.Portable;
 
     /// <summary>
@@ -58,7 +58,7 @@ namespace Apache.Ignite.Core.Impl.Portable
         private readonly string _affKeyFieldName;
 
         /** Type structure. */
-        private volatile PortableTypeStructure _typeStruct = 
PortableTypeStructure.CreateEmpty();
+        private volatile PortableStructure _typeStruct = 
PortableStructure.CreateEmpty();
 
         /// <summary>
         /// Constructor.
@@ -178,14 +178,14 @@ namespace Apache.Ignite.Core.Impl.Portable
         }
 
         /** <inheritDoc /> */
-        public PortableTypeStructure TypeStructure
+        public PortableStructure TypeStructure
         {
             get { return _typeStruct; }
         }
 
         /** <inheritDoc /> */
-        public void UpdateStrcuture(PortableTypeStructure exp, int pathIdx, 
-            IList<PortableTypeStructureUpdate> updates)
+        public void UpdateStrcuture(PortableStructure exp, int pathIdx, 
+            IList<PortableStructureUpdate> updates)
         {
             lock (this)
             {

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
index adbb6bb..103dd75 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
@@ -19,7 +19,7 @@ namespace Apache.Ignite.Core.Impl.Portable
 {
     using System;
     using System.Collections.Generic;
-    using Apache.Ignite.Core.Impl.Portable.Metadata.Opto;
+    using Apache.Ignite.Core.Impl.Portable.Structure;
     using Apache.Ignite.Core.Portable;
 
     /// <summary>
@@ -38,7 +38,7 @@ namespace Apache.Ignite.Core.Impl.Portable
         private readonly string _name;
 
         /** Type structure. */
-        private volatile PortableTypeStructure _typeStruct = 
PortableTypeStructure.CreateEmpty();
+        private volatile PortableStructure _typeStruct = 
PortableStructure.CreateEmpty();
 
         /// <summary>
         /// Constructor.
@@ -125,14 +125,14 @@ namespace Apache.Ignite.Core.Impl.Portable
         }
 
         /** <inheritDoc /> */
-        public PortableTypeStructure TypeStructure
+        public PortableStructure TypeStructure
         {
             get { return _typeStruct; }
         }
 
         /** <inheritDoc /> */
-        public void UpdateStrcuture(PortableTypeStructure exp, int pathIdx,
-            IList<PortableTypeStructureUpdate> updates)
+        public void UpdateStrcuture(PortableStructure exp, int pathIdx,
+            IList<PortableStructureUpdate> updates)
         {
             lock (this)
             {

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
index 9727377..cafc69d 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
@@ -24,7 +24,7 @@ namespace Apache.Ignite.Core.Impl.Portable
 
     using Apache.Ignite.Core.Impl.Portable.IO;
     using Apache.Ignite.Core.Impl.Portable.Metadata;
-    using Apache.Ignite.Core.Impl.Portable.Metadata.Opto;
+    using Apache.Ignite.Core.Impl.Portable.Structure;
     using Apache.Ignite.Core.Portable;
 
     using PU = PortableUtils;
@@ -61,10 +61,17 @@ namespace Apache.Ignite.Core.Impl.Portable
         /** Current raw position. */
         private long _curRawPos;
 
-        private PortableTypeStructure _curTypeStruct;
-        private int _curPathIdx;
-        private int _curActionIdx;
-        private List<PortableTypeStructureUpdate> _curUpdates; 
+        /** Current type structure. */
+        private PortableStructure _curStruct;
+
+        /** Current type structure path index. */
+        private int _curStructPath;
+
+        /** Current type structure action index. */
+        private int _curStructAction;
+
+        /** Current type structure updates. */
+        private List<PortableStructureUpdate> _curStructUpdates; 
         
         /** Whether we are currently detaching an object. */
         private bool _detaching;
@@ -1306,10 +1313,10 @@ namespace Apache.Ignite.Core.Impl.Portable
                 IPortableIdMapper oldMapper = _curMapper;
                 long oldRawPos = _curRawPos;
                 
-                PortableTypeStructure oldTypeStruct = _curTypeStruct;
-                int oldPathIdx = _curPathIdx;
-                int oldActionIdx = _curActionIdx;
-                var oldUpdates = _curUpdates;
+                PortableStructure oldStruct = _curStruct;
+                int oldStructPath = _curStructPath;
+                int oldStructIdx = _curStructAction;
+                var oldStructUpdates = _curStructUpdates;
 
                 // Push new frame.
                 _curTypeId = desc.TypeId;
@@ -1317,10 +1324,10 @@ namespace Apache.Ignite.Core.Impl.Portable
                 _curMapper = desc.Mapper;
                 _curRawPos = 0;
 
-                _curTypeStruct = desc.TypeStructure;
-                _curPathIdx = 0;
-                _curActionIdx = 0;
-                _curUpdates = null;
+                _curStruct = desc.TypeStructure;
+                _curStructPath = 0;
+                _curStructAction = 0;
+                _curStructUpdates = null;
 
                 // Write object fields.
                 desc.Serializer.WritePortable(obj, this);
@@ -1335,16 +1342,16 @@ namespace Apache.Ignite.Core.Impl.Portable
                 else
                     _stream.WriteInt(pos + 14, len);
 
-                // 13. Apply structure updates if any.
-                if (_curUpdates != null)
+                // Apply structure updates if any.
+                if (_curStructUpdates != null)
                 {
-                    desc.UpdateStrcuture(_curTypeStruct, _curPathIdx, 
_curUpdates);
+                    desc.UpdateStrcuture(_curStruct, _curStructPath, 
_curStructUpdates);
 
                     IPortableMetadataHandler metaHnd = 
_marsh.MetadataHandler(desc);
 
                     if (metaHnd != null)
                     {
-                        foreach (var u in _curUpdates)
+                        foreach (var u in _curStructUpdates)
                             metaHnd.OnFieldWrite(u.FieldId, u.FieldName, 
u.FieldType);
 
                         IDictionary<string, int> meta = 
metaHnd.OnObjectWriteFinished();
@@ -1360,10 +1367,10 @@ namespace Apache.Ignite.Core.Impl.Portable
                 _curMapper = oldMapper;
                 _curRawPos = oldRawPos;
 
-                _curTypeStruct = oldTypeStruct;
-                _curPathIdx = oldPathIdx;
-                _curActionIdx = oldActionIdx;
-                _curUpdates = oldUpdates;
+                _curStruct = oldStruct;
+                _curStructPath = oldStructPath;
+                _curStructAction = oldStructIdx;
+                _curStructUpdates = oldStructUpdates;
             }
             else
             {
@@ -1620,19 +1627,19 @@ namespace Apache.Ignite.Core.Impl.Portable
             if (_curRawPos != 0)
                 throw new PortableException("Cannot write named fields after 
raw data is written.");
 
-            int actionIdx = _curActionIdx++;
+            int action = _curStructAction++;
 
             int fieldId;
 
-            if (_curUpdates == null)
+            if (_curStructUpdates == null)
             {
-                fieldId = _curTypeStruct.GetFieldId(fieldName, fieldTypeId, 
ref _curPathIdx, actionIdx);
+                fieldId = _curStruct.GetFieldId(fieldName, fieldTypeId, ref 
_curStructPath, action);
 
                 if (fieldId == 0)
-                    fieldId = GetNewFieldId(fieldName, fieldTypeId, actionIdx);
+                    fieldId = GetNewFieldId(fieldName, fieldTypeId, action);
             }
             else
-                fieldId = GetNewFieldId(fieldName, fieldTypeId, actionIdx);
+                fieldId = GetNewFieldId(fieldName, fieldTypeId, action);
 
             _stream.WriteInt(fieldId);
         }
@@ -1640,15 +1647,18 @@ namespace Apache.Ignite.Core.Impl.Portable
         /// <summary>
         /// Get ID for the new field and save structure update.
         /// </summary>
+        /// <param name="fieldName">Field name.</param>
+        /// <param name="fieldTypeId">Field type ID.</param>
+        /// <param name="action">Action index.</param>
         /// <returns>Field ID.</returns>
-        private int GetNewFieldId(string fieldName, byte fieldTypeId, int 
actionIdx)
+        private int GetNewFieldId(string fieldName, byte fieldTypeId, int 
action)
         {
             int fieldId = PU.FieldId(_curTypeId, fieldName, _curConverter, 
_curMapper);
 
-            if (_curUpdates == null)
-                _curUpdates = new List<PortableTypeStructureUpdate>();
+            if (_curStructUpdates == null)
+                _curStructUpdates = new List<PortableStructureUpdate>();
 
-            _curUpdates.Add(new PortableTypeStructureUpdate(fieldName, 
fieldId, fieldTypeId, actionIdx));
+            _curStructUpdates.Add(new PortableStructureUpdate(fieldName, 
fieldId, fieldTypeId, action));
 
             return fieldId;
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs
new file mode 100644
index 0000000..c434110
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Portable.Structure
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Diagnostics;
+
+    using Apache.Ignite.Core.Portable;
+
+    /// <summary>
+    /// Portable type structure. Cache field IDs and metadata to improve 
marshalling performance.
+    /// Every object write contains a set of field writes. Every unique 
ordered set of written fields
+    /// produce write "path". We cache these paths allowing for very fast 
traverse over object structure
+    /// without expensive map lookups and field ID calculations. 
+    /// </summary>
+    internal class PortableStructure
+    {
+        /// <summary>
+        /// Create empty type structure.
+        /// </summary>
+        /// <returns>Empty type structure.</returns>
+        public static PortableStructure CreateEmpty()
+        {
+            return new PortableStructure(new[] { new PortableStructureEntry[0] 
}, 
+                new PortableStructureJumpTable[1], new Dictionary<string, 
byte>());
+        }
+
+        /** Entries. */
+        private readonly PortableStructureEntry[][] _paths;
+
+        /** Jumps. */
+        private readonly PortableStructureJumpTable[] _jumps;
+
+        /** Field types. */
+        private readonly IDictionary<string, byte> _fieldTypes; 
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="paths">Paths.</param>
+        /// <param name="jumps">Jumps.</param>
+        /// <param name="fieldTypes">Field types.</param>
+        private PortableStructure(PortableStructureEntry[][] paths,
+            PortableStructureJumpTable[] jumps, IDictionary<string, byte> 
fieldTypes)
+        {
+            _paths = paths;
+            _jumps = jumps;
+            _fieldTypes = fieldTypes;
+        }
+
+        /// <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(pathIdx <= _paths.Length);
+
+            // Get path.
+            PortableStructureEntry[] path = _paths[pathIdx];
+
+            if (actionIdx < path.Length)
+            {
+                // Get entry matching the action index.
+                PortableStructureEntry entry = path[actionIdx];
+
+                if (entry.IsExpected(fieldName, fieldType))
+                    // Entry matches our expectations, return.
+                    return entry.Id;
+                else if (entry.IsJumpTable)
+                {
+                    // Entry is a pointer to a jump table.
+                    Debug.Assert(entry.Id < _jumps.Length);
+
+                    PortableStructureJumpTable jmpTbl = _jumps[entry.Id];
+
+                    int pathIdx0 = jmpTbl.GetPathIndex(fieldName);
+
+                    if (pathIdx0 < 0)
+                        return 0;
+
+                    Debug.Assert(pathIdx < _paths.Length);
+
+                    entry = _paths[pathIdx][actionIdx];
+
+                    entry.ValidateType(fieldType);
+
+                    pathIdx = pathIdx0;
+
+                    return entry.Id;
+                }
+            }
+
+            // Failed to find anything because this is a new field.
+            return 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 PortableStructure Merge(PortableStructure exp, int pathIdx, 
+            IList<PortableStructureUpdate> 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.
+            PortableStructure res = MergeFieldTypes(updates);
+
+            if (ReferenceEquals(exp, this))
+            {
+                PortableStructureUpdate 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);
+
+                    PortableStructureEntry[][] newPaths = 
CopyPaths(updates.Count, 0);
+
+                    ApplyUpdatesToPath(newPaths[0], updates);
+
+                    res = new PortableStructure(newPaths, _jumps, 
res._fieldTypes);
+                }
+                else
+                {
+                    // Get entry where updates should start.
+                    PortableStructureEntry[] path = _paths[pathIdx];
+
+                    PortableStructureEntry startEntry = 
default(PortableStructureEntry);
+
+                    if (firstUpdate.Index < path.Length)
+                        startEntry = path[firstUpdate.Index];
+
+                    if (startEntry.IsEmpty)
+                    {
+                        // We are on the empty/non-existend entry. Continue 
the path without branching.
+                        var newPaths = CopyPaths(firstUpdate.Index + 
updates.Count, 0);
+
+                        ApplyUpdatesToPath(newPaths[pathIdx], updates);
+
+                        res = new PortableStructure(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. Preapare 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 updats to the new path.
+                        ApplyUpdatesToPath(newPaths[newPathIdx], updates);
+
+                        // Add new jump to the table.
+                        newJumps[startEntry.Id] = 
+                            
newJumps[startEntry.Id].CopyAndAdd(firstUpdate.FieldName, newPathIdx);
+
+                        res = new PortableStructure(newPaths, newJumps, 
res._fieldTypes);
+                    }
+                    else
+                    {
+                        // We are on existing entry. Need to create a new jump 
table here and two new paths.
+
+                        // 1. Preapare 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 
PortableStructureJumpTable(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];
+
+                            newPaths[pathIdx][i] = new 
PortableStructureEntry();
+                        }
+
+                        // Apply updats to the new path.
+                        ApplyUpdatesToPath(newPaths[newPaths.Length - 1], 
updates);
+
+                        res = new PortableStructure(newPaths, newJumps, 
res._fieldTypes);
+                    }
+
+                }
+            }
+
+            return res;
+        }
+
+        /// <summary>
+        /// Copy and possible expand paths.
+        /// </summary>
+        /// <param name="minLen">Minimum length.</param>
+        /// <param name="additionalPaths">Amount of additional paths 
required.</param>
+        /// <returns>Result.</returns>
+        private PortableStructureEntry[][] CopyPaths(int minLen, int 
additionalPaths)
+        {
+            var newPaths = new PortableStructureEntry[_paths.Length + 
additionalPaths][];
+
+            int newPathLen = Math.Max(_paths[0].Length, minLen);
+
+            for (int i = 0; i < _paths.Length; i++)
+            {
+                newPaths[i] = new PortableStructureEntry[newPathLen];
+
+                Array.Copy(_paths[i], newPaths[i], _paths[i].Length);
+            }
+
+            return newPaths;
+        }
+
+        /// <summary>
+        /// Copy and possible expand jump tables.
+        /// </summary>
+        /// <param name="additionalJumps">Additional jumps.</param>
+        /// <returns>Result.</returns>
+        private PortableStructureJumpTable[] CopyJumps(int additionalJumps)
+        {
+            var newJumps = new PortableStructureJumpTable[_jumps.Length + 
additionalJumps];
+
+            for (int i = 0; i < _jumps.Length; i++)
+                newJumps[i] = _jumps[i].Copy();
+
+            return newJumps;
+        }
+
+        /// <summary>
+        /// Apply updates to path.
+        /// </summary>
+        /// <param name="path">Path.</param>
+        /// <param name="updates">Updates.</param>
+        private static void ApplyUpdatesToPath(IList<PortableStructureEntry> 
path,
+            IEnumerable<PortableStructureUpdate> updates)
+        {
+            foreach (var u in updates)
+                path[u.Index] = new PortableStructureEntry(u.FieldName, 
u.FieldId, u.FieldType);
+        }
+
+        /// <summary>
+        /// Merge field types.
+        /// </summary>
+        /// <param name="updates">Updates.</param>
+        /// <returns>Type structure with applied updates.</returns>
+        private PortableStructure 
MergeFieldTypes(IList<PortableStructureUpdate> updates)
+        {
+            IDictionary<string, byte> newFieldTypes = new Dictionary<string, 
byte>(_fieldTypes);
+
+            foreach (PortableStructureUpdate update in updates)
+            {
+                byte expType;
+
+                if (_fieldTypes.TryGetValue(update.FieldName, out expType))
+                {
+                    // This is an old field.
+                    if (expType != update.FieldType)
+                    {
+                        throw new PortableException("Field type mismatch 
detected [fieldName=" + update.FieldName +
+                            ", expectedType=" + expType + ", actualType=" + 
update.FieldType + ']');
+                    }
+                }
+                else
+                    // This is a new field.
+                    newFieldTypes[update.FieldName] = update.FieldType;
+            }
+
+            return newFieldTypes.Count == _fieldTypes.Count ? 
+                this : new PortableStructure(_paths, _jumps, newFieldTypes);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs
new file mode 100644
index 0000000..5229a95
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Portable.Structure
+{
+    using System.Diagnostics;
+
+    using Apache.Ignite.Core.Portable;
+
+    /// <summary>
+    /// Portable type structure entry. Might be either a normal field or a 
reference to jump table.
+    /// </summary>
+    internal struct PortableStructureEntry
+    {
+        /** Field name. */
+        private readonly string _name;
+
+        /** Field ID. */
+        private readonly int _id;
+
+        /** Field type. */
+        private readonly byte _type;
+        
+        /// <summary>
+        /// Constructor for jump table entry.
+        /// </summary>
+        /// <param name="jumpTblIdx">Jump table index.</param>
+        public PortableStructureEntry(int jumpTblIdx)
+        {
+            Debug.Assert(jumpTblIdx > 0);
+
+            _name = null;
+            _id = jumpTblIdx;
+            _type = 0;
+        }
+
+        /// <summary>
+        /// Constructor for field entry.
+        /// </summary>
+        /// <param name="name">Field name.</param>
+        /// <param name="id">Field ID.</param>
+        /// <param name="type">Field type.</param>
+        public PortableStructureEntry(string name, int id, byte type)
+        {
+            Debug.Assert(name != null);
+
+            _name = name;
+            _id = id;
+            _type = type;
+        }
+
+        /// <summary>
+        /// Check whether current field entry matches passed arguments.
+        /// </summary>
+        /// <param name="name"></param>
+        /// <param name="type"></param>
+        /// <returns></returns>
+        public bool IsExpected(string name, byte type)
+        {
+            if (!ReferenceEquals(_name, name) && !_name.Equals(name))
+                return false;
+
+            ValidateType(type);
+
+            return true;
+        }
+
+        /// <summary>
+        /// Valide field type.
+        /// </summary>
+        /// <param name="type">Expected type.</param>
+        public void ValidateType(byte type)
+        {
+            if (_type != type)
+            {
+                throw new PortableException("Field type mismatch detected 
[fieldName=" + _name +
+                    ", expectedType=" + _type + ", actualType=" + type + ']');
+            }
+        }
+
+        /// <summary>
+        /// Whether this is an empty entry.
+        /// </summary>
+        /// <returns></returns>
+        public bool IsEmpty
+        {
+            get { return _id == 0; }
+        }
+
+        /// <summary>
+        /// Whether this is a jump table.
+        /// </summary>
+        public bool IsJumpTable
+        {
+            get { return _name == null && _id >= 0; }
+        }
+
+        /// <summary>
+        /// Field name.
+        /// </summary>
+        public string Name
+        {
+            get { return _name; }
+        }
+
+        /// <summary>
+        /// Field ID.
+        /// </summary>
+        public int Id
+        {
+            get { return _id; }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureJumpTable.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureJumpTable.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureJumpTable.cs
new file mode 100644
index 0000000..85e71c4
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureJumpTable.cs
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Portable.Structure
+{
+    using System;
+    using System.Diagnostics;
+
+    /// <summary>
+    /// Jump table.
+    /// </summary>
+    internal class PortableStructureJumpTable
+    {
+        /** Names. */
+        private readonly string[] _names;
+
+        /** Path indexes. */
+        private readonly int[] _pathIdxs;
+
+        /// <summary>
+        /// Create jump table with two entries.
+        /// </summary>
+        /// <param name="firstName">First name.</param>
+        /// <param name="firstPathIdx">First path index.</param>
+        /// <param name="secondName">Second name.</param>
+        /// <param name="secondPathIdx">Second path index.</param>
+        public PortableStructureJumpTable(string firstName, int firstPathIdx, 
+            string secondName, int secondPathIdx)
+        {
+            _names = new[] { firstName, secondName };
+            _pathIdxs = new[] { firstPathIdx, secondPathIdx };
+        }
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="names">Field names.</param>
+        /// <param name="pathIdxs">Path indexes.</param>
+        private PortableStructureJumpTable(string[] names, int[] pathIdxs)
+        {
+            Debug.Assert(_names.Length > 1);
+            Debug.Assert(_names.Length == pathIdxs.Length);
+            
+            _names = names;
+            _pathIdxs = pathIdxs;
+        }
+
+        /// <summary>
+        /// Get path index for the given field.
+        /// </summary>
+        /// <param name="fieldName">Field name.</param>
+        /// <returns>Path index.</returns>
+        public int GetPathIndex(string fieldName)
+        {
+            Debug.Assert(fieldName != null);
+            
+            // Optimistically assume that field name is a literal.
+            for (var i = 0; i < _names.Length; i++)
+            {
+                if (ReferenceEquals(fieldName, _names[i]))
+                    return _pathIdxs[i];
+            }
+
+            // Fallback to slow-path with normal string comparison.
+            for (var i = 0; i < _names.Length; i++)
+            {
+                if (fieldName.Equals(_names[i]))
+                    return _pathIdxs[i];
+            }
+
+            // No path found for the field.
+            return -1;
+        }
+
+        /// <summary>
+        /// Copy jump table.
+        /// </summary>
+        /// <returns>New jump table.</returns>
+        public PortableStructureJumpTable Copy()
+        {
+            return new PortableStructureJumpTable(_names, _pathIdxs);
+        }
+
+        /// <summary>
+        /// Copy jump table with additional jump.
+        /// </summary>
+        /// <param name="name">Field name.</param>
+        /// <param name="pathIdx">Path index.</param>
+        /// <returns>New jump table.</returns>
+        public PortableStructureJumpTable CopyAndAdd(string name, int pathIdx)
+        {
+            var newNames = new string[_names.Length + 1];
+            var pathIdxs = new int[_pathIdxs.Length + 1];
+
+            Array.Copy(_names, newNames, _names.Length);
+            Array.Copy(_pathIdxs, pathIdxs, _pathIdxs.Length);
+
+            return new PortableStructureJumpTable(newNames, pathIdxs);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/b34084eb/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureUpdate.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureUpdate.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureUpdate.cs
new file mode 100644
index 0000000..fa239db
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureUpdate.cs
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Portable.Structure
+{
+    /// <summary>
+    /// Portable type structure update descriptor.
+    /// </summary>
+    internal class PortableStructureUpdate
+    {
+        /** Field name. */
+        private readonly string _fieldName;
+
+        /** Field ID. */
+        private readonly int _fieldId;
+
+        /** Field type. */
+        private readonly byte _fieldType;
+
+        /** Field index. */
+        private readonly int _idx;
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="fieldName">Field name.</param>
+        /// <param name="fieldId">Field ID.</param>
+        /// <param name="fieldType">Field type.</param>
+        /// <param name="idx">Index.</param>
+        public PortableStructureUpdate(string fieldName, int fieldId, byte 
fieldType, int idx)
+        {
+            _fieldName = fieldName;
+            _fieldId = fieldId;
+            _fieldType = fieldType;
+            _idx = idx;
+        }
+
+        /// <summary>
+        /// Field name.
+        /// </summary>
+        public string FieldName
+        {
+            get { return _fieldName; }
+        }
+
+        /// <summary>
+        /// Field ID.
+        /// </summary>
+        public int FieldId
+        {
+            get { return _fieldId; }
+        }
+
+        /// <summary>
+        /// Field type.
+        /// </summary>
+        public byte FieldType
+        {
+            get { return _fieldType; }
+        }
+
+        /// <summary>
+        /// Index.
+        /// </summary>
+        public int Index
+        {
+            get { return _idx; }
+        }
+    }
+}

Reply via email to