Repository: ignite
Updated Branches:
  refs/heads/ignite-1282 49c495b9c -> 4e053e45d


IGNITE-1420: Optimized fields calculation and metadata handling.


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

Branch: refs/heads/ignite-1282
Commit: 4e053e45dceab7b700ef0b83498219018c0d33e9
Parents: 49c495b
Author: vozerov-gridgain <voze...@gridgain.com>
Authored: Wed Oct 7 15:00:46 2015 +0300
Committer: vozerov-gridgain <voze...@gridgain.com>
Committed: Wed Oct 7 15:00:46 2015 +0300

----------------------------------------------------------------------
 .../Portable/PortableWriteBenchmark.cs          |   2 +-
 .../Apache.Ignite.Core.Tests.csproj             |   1 +
 .../Portable/PortableStructureTest.cs           | 261 +++++++++++++++
 .../Apache.Ignite.Core.csproj                   |   4 +
 .../Impl/Portable/IPortableTypeDescriptor.cs    |  16 +
 .../Impl/Portable/PortableFullTypeDescriptor.cs |  22 ++
 .../Impl/Portable/PortableMarshaller.cs         |   1 +
 .../Portable/PortableSurrogateTypeDescriptor.cs |  24 +-
 .../Impl/Portable/PortableUtils.cs              |   4 +
 .../Impl/Portable/PortableWriterImpl.cs         |  88 ++++-
 .../Portable/Structure/PortableStructure.cs     | 333 +++++++++++++++++++
 .../Structure/PortableStructureEntry.cs         | 129 +++++++
 .../Structure/PortableStructureJumpTable.cs     | 118 +++++++
 .../Structure/PortableStructureUpdate.cs        |  84 +++++
 14 files changed, 1071 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs
index 9fcfa46..5638195 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs
@@ -46,7 +46,7 @@ namespace Apache.Ignite.Benchmarks.Portable
             {
                 TypeConfigurations = new List<PortableTypeConfiguration>
                 {
-                    new PortableTypeConfiguration(typeof (Address)) 
{MetadataEnabled = false}
+                    new PortableTypeConfiguration(typeof (Address)) 
{MetadataEnabled = true}
                 }
             });
         }

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
index 90f3481..7cbe784 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Apache.Ignite.Core.Tests.csproj
@@ -111,6 +111,7 @@
     <Compile Include="MarshallerTest.cs" />
     <Compile Include="MessagingTest.cs" />
     <Compile Include="PortableConfigurationTest.cs" />
+    <Compile Include="Portable\PortableStructureTest.cs" />
     <Compile Include="SerializationTest.cs" />
     <Compile Include="IgniteStartStopTest.cs" />
     <Compile Include="TestUtils.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableStructureTest.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableStructureTest.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableStructureTest.cs
new file mode 100644
index 0000000..1212ea0
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableStructureTest.cs
@@ -0,0 +1,261 @@
+/*
+ * 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.Tests.Portable
+{
+    using System;
+    using System.Collections.Generic;
+    using Apache.Ignite.Core.Impl;
+    using Apache.Ignite.Core.Impl.Portable;
+    using Apache.Ignite.Core.Impl.Portable.Structure;
+    using Apache.Ignite.Core.Portable;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// Contains tests for portable type structure.
+    /// </summary>
+    [TestFixture]
+    public class PortableStructureTest
+    {
+        /** Repeat count. */
+        public static readonly int RepeatCnt = 10;
+
+        /** Objects per mode. */
+        public static readonly int ObjectsPerMode = 5;
+
+        /// <summary>
+        /// Test object write with different structures.
+        /// </summary>
+        [Test]
+        public void TestStructure()
+        {
+            for (int i = 1; i <= RepeatCnt; i++)
+            {
+                Console.WriteLine(">>> Iteration started: " + i);
+
+                // 1. Generate and shuffle objects.
+                IList<BranchedType> objs = new List<BranchedType>();
+
+                for (int j = 0; j < 6 * ObjectsPerMode; j++)
+                    objs.Add(new BranchedType((j%6) + 1));
+
+                objs = IgniteUtils.Shuffle(objs);
+
+                // 2. Create new marshaller.
+                PortableTypeConfiguration typeCfg = new 
PortableTypeConfiguration(typeof(BranchedType));
+
+                PortableConfiguration cfg = new PortableConfiguration
+                {
+                    TypeConfigurations = new List<PortableTypeConfiguration> { 
typeCfg }
+                };
+
+                PortableMarshaller marsh = new PortableMarshaller(cfg);
+
+                // 3. Marshal all data and ensure deserialized object is fine.
+                foreach (BranchedType obj in objs)
+                {
+                    Console.WriteLine(">>> Write object [mode=" + obj.mode + 
']');
+
+                    byte[] data = marsh.Marshal(obj);
+
+                    BranchedType other = marsh.Unmarshal<BranchedType>(data);
+
+                    Assert.IsTrue(obj.Equals(other));
+                }
+                
+                Console.WriteLine();
+
+                // 4. Ensure that all fields are recorded.
+                IPortableTypeDescriptor desc = marsh.Descriptor(typeof 
(BranchedType));
+
+                PortableStructure typeStruct = desc.TypeStructure;
+
+                IDictionary<string, byte> fields = typeStruct.FieldTypes;
+
+                Assert.IsTrue(fields.Count == 8);
+
+                Assert.IsTrue(fields.ContainsKey("mode"));
+                Assert.IsTrue(fields.ContainsKey("f2"));
+                Assert.IsTrue(fields.ContainsKey("f3"));
+                Assert.IsTrue(fields.ContainsKey("f4"));
+                Assert.IsTrue(fields.ContainsKey("f5"));
+                Assert.IsTrue(fields.ContainsKey("f6"));
+                Assert.IsTrue(fields.ContainsKey("f7"));
+                Assert.IsTrue(fields.ContainsKey("f8"));
+            }
+        }
+    }
+
+    public class BranchedType : IPortableMarshalAware
+    {
+        public int mode;
+        public int f2;
+        public int f3;
+        public int f4;
+        public int f5;
+        public int f6;
+        public int f7;
+        public int f8;
+
+        public BranchedType(int mode)
+        {
+            this.mode = mode;
+
+            switch (mode)
+            {
+                case 1:
+                    f2 = 2;
+
+                    break;
+
+                case 2:
+                    f2 = 2;
+                    f3 = 3;
+                    f4 = 4;
+
+                    break;
+
+                case 3:
+                    f2 = 2;
+                    f3 = 3;
+                    f5 = 5;
+
+                    break;
+
+                case 4:
+                    f2 = 2;
+                    f3 = 3;
+                    f5 = 5;
+                    f6 = 6;
+
+                    break;
+
+                case 5:
+                    f2 = 2;
+                    f3 = 3;
+                    f7 = 7;
+
+                    break;
+
+                case 6:
+                    f8 = 8;
+
+                    break;
+            }
+        }
+
+        public void WritePortable(IPortableWriter writer)
+        {
+            writer.WriteInt("mode", mode);
+
+            switch (mode)
+            {
+                case 1:
+                    writer.WriteInt("f2", f2);
+
+                    break;
+
+                case 2:
+                    writer.WriteInt("f2", f2);
+                    writer.WriteInt("f3", f3);
+                    writer.WriteInt("f4", f4);
+
+                    break;
+
+                case 3:
+                    writer.WriteInt("f2", f2);
+                    writer.WriteInt("f3", f3);
+                    writer.WriteInt("f5", f5);
+
+                    break;
+
+                case 4:
+                    writer.WriteInt("f2", f2);
+                    writer.WriteInt("f3", f3);
+                    writer.WriteInt("f5", f5);
+                    writer.WriteInt("f6", f6);
+
+                    break;
+
+                case 5:
+                    writer.WriteInt("f2", f2);
+                    writer.WriteInt("f3", f3);
+                    writer.WriteInt("f7", f7);
+
+                    break;
+
+                case 6:
+                    writer.WriteInt("f8", f8);
+
+                    break;
+            }
+        }
+
+        public void ReadPortable(IPortableReader reader)
+        {
+            mode = reader.ReadInt("mode");
+
+            switch (mode)
+            {
+                case 1:
+                    f2 = reader.ReadInt("f2");
+
+                    break;
+
+                case 2:
+                    f2 = reader.ReadInt("f2");
+                    f3 = reader.ReadInt("f3");
+                    f4 = reader.ReadInt("f4");
+
+                    break;
+
+                case 3:
+                    f2 = reader.ReadInt("f2");
+                    f3 = reader.ReadInt("f3");
+                    f5 = reader.ReadInt("f5");
+
+                    break;
+
+                case 4:
+                    f2 = reader.ReadInt("f2");
+                    f3 = reader.ReadInt("f3");
+                    f5 = reader.ReadInt("f5");
+                    f6 = reader.ReadInt("f6");
+
+                    break;
+
+                case 5:
+                    f2 = reader.ReadInt("f2");
+                    f3 = reader.ReadInt("f3");
+                    f7 = reader.ReadInt("f7");
+
+                    break;
+
+                case 6:
+                    f8 = reader.ReadInt("f8");
+
+                    break;
+            }
+        }
+
+        public bool Equals(BranchedType other)
+        {
+            return mode == other.mode && f2 == other.f2 && f3 == other.f3 && 
f4 == other.f4 && f5 == other.f5 &&
+                   f6 == other.f6 && f7 == other.f7 && f8 == other.f8;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/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 855dda8..848ce49 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -275,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/4e053e45/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 62597d5..81b6c8d 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
@@ -18,6 +18,9 @@
 namespace Apache.Ignite.Core.Impl.Portable
 {
     using System;
+    using System.Collections.Generic;
+
+    using Apache.Ignite.Core.Impl.Portable.Structure;
     using Apache.Ignite.Core.Portable;
 
     /// <summary>
@@ -104,5 +107,18 @@ namespace Apache.Ignite.Core.Impl.Portable
         {
             get;
         }
+
+        /// <summary>
+        /// Type structure.
+        /// </summary>
+        PortableStructure TypeStructure { get; }
+
+        /// <summary>
+        /// Update type structure.
+        /// </summary>
+        /// <param name="exp">Expected type structure.</param>
+        /// <param name="pathIdx">Path index.</param>
+        /// <param name="updates">Recorded updates.</param>
+        void UpdateStructure(PortableStructure exp, int pathIdx, 
IList<PortableStructureUpdate> updates);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/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 79b860f..ecad8ba 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
@@ -18,6 +18,9 @@
 namespace Apache.Ignite.Core.Impl.Portable
 {
     using System;
+    using System.Collections.Generic;
+
+    using Apache.Ignite.Core.Impl.Portable.Structure;
     using Apache.Ignite.Core.Portable;
 
     /// <summary>
@@ -55,6 +58,9 @@ namespace Apache.Ignite.Core.Impl.Portable
         /** Affinity field key name. */
         private readonly string _affKeyFieldName;
 
+        /** Type structure. */
+        private volatile PortableStructure _typeStruct = 
PortableStructure.CreateEmpty();
+
         /// <summary>
         /// Constructor.
         /// </summary>
@@ -171,5 +177,21 @@ namespace Apache.Ignite.Core.Impl.Portable
         {
             get { return _affKeyFieldName; }
         }
+
+        /** <inheritDoc /> */
+        public PortableStructure TypeStructure
+        {
+            get { return _typeStruct; }
+        }
+
+        /** <inheritDoc /> */
+        public void UpdateStructure(PortableStructure exp, int pathIdx, 
+            IList<PortableStructureUpdate> updates)
+        {
+            lock (this)
+            {
+                _typeStruct = _typeStruct.Merge(exp, pathIdx, updates);
+            }
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableMarshaller.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableMarshaller.cs
 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableMarshaller.cs
index c7a0b7b..c7262e1 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableMarshaller.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableMarshaller.cs
@@ -309,6 +309,7 @@ namespace Apache.Ignite.Core.Impl.Portable
 
                 return new PortableHashsetMetadataHandler(ids, newType);
             }
+
             return null;
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/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 9842c46..8dc18b6 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
@@ -18,10 +18,14 @@
 namespace Apache.Ignite.Core.Impl.Portable
 {
     using System;
+    using System.Collections.Generic;
+
+    using Apache.Ignite.Core.Impl.Portable.Structure;
     using Apache.Ignite.Core.Portable;
 
     /// <summary>
-    /// Surrogate type descriptor. Used in cases when type if identified by 
name and is not provided in configuration.
+    /// Surrogate type descriptor. Used in cases when type if identified by 
name and 
+    /// is not provided in configuration.
     /// </summary>
     internal class PortableSurrogateTypeDescriptor : IPortableTypeDescriptor
     {
@@ -34,6 +38,9 @@ namespace Apache.Ignite.Core.Impl.Portable
         /** Type name. */
         private readonly string _name;
 
+        /** Type structure. */
+        private volatile PortableStructure _typeStruct = 
PortableStructure.CreateEmpty();
+
         /// <summary>
         /// Constructor.
         /// </summary>
@@ -117,5 +124,20 @@ namespace Apache.Ignite.Core.Impl.Portable
         {
             get { return null; }
         }
+
+        /** <inheritDoc /> */
+        public PortableStructure TypeStructure
+        {
+            get { return _typeStruct; }
+        }
+
+        /** <inheritDoc /> */
+        public void UpdateStructure(PortableStructure exp, int pathIdx, 
IList<PortableStructureUpdate> updates)
+        {
+            lock (this)
+            {
+                _typeStruct = _typeStruct.Merge(exp, pathIdx, updates);
+            }
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs
----------------------------------------------------------------------
diff --git 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs
index 2344db2..fb0b195 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs
@@ -1799,6 +1799,10 @@ namespace Apache.Ignite.Core.Impl.Portable
             if (id == 0)
                 id = StringHashCode(fieldName);
 
+            if (id == 0)
+                throw new PortableException("Field ID is zero (please provide 
ID mapper or change field name) " + 
+                    "[typeId=" + typeId + ", fieldName=" + fieldName + ", 
idMapper=" + idMapper + ']');
+
             return id;
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/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 69523c9..0495b68 100644
--- 
a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
@@ -24,6 +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.Structure;
     using Apache.Ignite.Core.Portable;
 
     using PU = PortableUtils;
@@ -56,12 +57,21 @@ namespace Apache.Ignite.Core.Impl.Portable
 
         /** Current mapper. */
         private IPortableIdMapper _curMapper;
-
-        /** Current metadata handler. */
-        private IPortableMetadataHandler _curMetaHnd;
         
         /** Current raw position. */
         private long _curRawPos;
+
+        /** 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;
@@ -1301,16 +1311,24 @@ namespace Apache.Ignite.Core.Impl.Portable
                 int oldTypeId = _curTypeId;
                 IPortableNameMapper oldConverter = _curConverter;
                 IPortableIdMapper oldMapper = _curMapper;
-                IPortableMetadataHandler oldMetaHnd = _curMetaHnd;
                 long oldRawPos = _curRawPos;
+                
+                PortableStructure oldStruct = _curStruct;
+                int oldStructPath = _curStructPath;
+                int oldStructAction = _curStructAction;
+                var oldStructUpdates = _curStructUpdates;
 
                 // Push new frame.
                 _curTypeId = desc.TypeId;
                 _curConverter = desc.NameConverter;
                 _curMapper = desc.Mapper;
-                _curMetaHnd = desc.MetadataEnabled ? 
_marsh.MetadataHandler(desc) : null;
                 _curRawPos = 0;
 
+                _curStruct = desc.TypeStructure;
+                _curStructPath = 0;
+                _curStructAction = 0;
+                _curStructUpdates = null;
+
                 // Write object fields.
                 desc.Serializer.WritePortable(obj, this);
 
@@ -1324,21 +1342,35 @@ namespace Apache.Ignite.Core.Impl.Portable
                 else
                     _stream.WriteInt(pos + 14, len);
 
-                // 13. Collect metadata.
-                if (_curMetaHnd != null)
+                // Apply structure updates if any.
+                if (_curStructUpdates != null)
                 {
-                    IDictionary<string, int> meta = 
_curMetaHnd.OnObjectWriteFinished();
+                    desc.UpdateStructure(_curStruct, _curStructPath, 
_curStructUpdates);
 
-                    if (meta != null)
-                        SaveMetadata(_curTypeId, desc.TypeName, 
desc.AffinityKeyFieldName, meta);
+                    IPortableMetadataHandler metaHnd = 
_marsh.MetadataHandler(desc);
+
+                    if (metaHnd != null)
+                    {
+                        foreach (var u in _curStructUpdates)
+                            metaHnd.OnFieldWrite(u.FieldId, u.FieldName, 
u.FieldType);
+
+                        IDictionary<string, int> meta = 
metaHnd.OnObjectWriteFinished();
+
+                        if (meta != null)
+                            SaveMetadata(_curTypeId, desc.TypeName, 
desc.AffinityKeyFieldName, meta);
+                    }
                 }
 
                 // Restore old frame.
                 _curTypeId = oldTypeId;
                 _curConverter = oldConverter;
                 _curMapper = oldMapper;
-                _curMetaHnd = oldMetaHnd;
                 _curRawPos = oldRawPos;
+
+                _curStruct = oldStruct;
+                _curStructPath = oldStructPath;
+                _curStructAction = oldStructAction;
+                _curStructUpdates = oldStructUpdates;
             }
             else
             {
@@ -1595,12 +1627,40 @@ namespace Apache.Ignite.Core.Impl.Portable
             if (_curRawPos != 0)
                 throw new PortableException("Cannot write named fields after 
raw data is written.");
 
-            int fieldId = PU.FieldId(_curTypeId, fieldName, _curConverter, 
_curMapper);
+            int action = _curStructAction++;
+
+            int fieldId;
+
+            if (_curStructUpdates == null)
+            {
+                fieldId = _curStruct.GetFieldId(fieldName, fieldTypeId, ref 
_curStructPath, action);
+
+                if (fieldId == 0)
+                    fieldId = GetNewFieldId(fieldName, fieldTypeId, action);
+            }
+            else
+                fieldId = GetNewFieldId(fieldName, fieldTypeId, action);
 
             _stream.WriteInt(fieldId);
+        }
+
+        /// <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 
action)
+        {
+            int fieldId = PU.FieldId(_curTypeId, fieldName, _curConverter, 
_curMapper);
+
+            if (_curStructUpdates == null)
+                _curStructUpdates = new List<PortableStructureUpdate>();
+
+            _curStructUpdates.Add(new PortableStructureUpdate(fieldName, 
fieldId, fieldTypeId, action));
 
-            if (_curMetaHnd != null)
-                _curMetaHnd.OnFieldWrite(fieldId, fieldName, fieldTypeId);
+            return fieldId;
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/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..2d47755
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs
@@ -0,0 +1,333 @@
+/*
+ * 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(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;
+        }
+
+        /// <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);
+
+                    var 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-existent 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. 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 PortableStructure(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 
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];
+
+                            if (i == firstUpdate.Index)
+                                // Inject jump table ...
+                                newPaths[pathIdx][i] = new 
PortableStructureEntry(newJumpIdx);
+                            else
+                                // ... or just reset.
+                                newPaths[pathIdx][i] = new 
PortableStructureEntry();
+                        }
+
+                        // Apply updates to the new path.
+                        ApplyUpdatesToPath(newPaths[newPaths.Length - 1], 
updates);
+
+                        res = new PortableStructure(newPaths, newJumps, 
res._fieldTypes);
+                    }
+
+                }
+            }
+
+            return res;
+        }
+
+        /// <summary>
+        /// Copy and possibly 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 < newPaths.Length; i++)
+            {
+                newPaths[i] = new PortableStructureEntry[newPathLen];
+
+                if (i < _paths.Length)
+                    Array.Copy(_paths[i], newPaths[i], _paths[i].Length);
+            }
+
+            return newPaths;
+        }
+
+        /// <summary>
+        /// Copy and possibly expand jump tables.
+        /// </summary>
+        /// <param name="additionalJumps">Amount of additional jumps 
required.</param>
+        /// <returns>Result.</returns>
+        private PortableStructureJumpTable[] CopyJumps(int additionalJumps)
+        {
+            var newJumps = new PortableStructureJumpTable[_jumps.Length + 
additionalJumps];
+
+            // The very first jump is always null so that we can distinguish 
between jump table
+            // and empty value in PortableStructureEntry.
+            for (int i = 1; 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);
+        }
+
+        /// <summary>
+        /// Recorded field types.
+        /// </summary>
+        internal IDictionary<string, byte> FieldTypes
+        {
+            get { return _fieldTypes; }
+        } 
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/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..9476635
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs
@@ -0,0 +1,129 @@
+/*
+ * 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, a 
reference to jump table, or an empty entry.
+    /// </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">Field name.</param>
+        /// <param name="type">Field type.</param>
+        /// <returns>True if expected.</returns>
+        public bool IsExpected(string name, byte type)
+        {
+            // Perform reference equality check first because field name is a 
literal in most cases.
+            if (!ReferenceEquals(_name, name) && !name.Equals(_name))
+                return false;
+
+            ValidateType(type);
+
+            return true;
+        }
+
+        /// <summary>
+        /// Validate 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/4e053e45/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..7f8b373
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureJumpTable.cs
@@ -0,0 +1,118 @@
+/*
+ * 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 minimal 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 newPathIdxs = new int[_pathIdxs.Length + 1];
+
+            Array.Copy(_names, newNames, _names.Length);
+            Array.Copy(_pathIdxs, newPathIdxs, _pathIdxs.Length);
+
+            newNames[newNames.Length - 1] = name;
+            newPathIdxs[newPathIdxs.Length - 1] = pathIdx;
+
+            return new PortableStructureJumpTable(newNames, newPathIdxs);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/4e053e45/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