Repository: ignite
Updated Branches:
  refs/heads/ignite-1282-opto [created] 3548457c2


IGNITE-1282: WIP on optos.


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

Branch: refs/heads/ignite-1282-opto
Commit: 9da45ed3e6f7c79a47bbd71fa3db64094cb96e68
Parents: 49c495b
Author: vozerov-gridgain <voze...@gridgain.com>
Authored: Wed Oct 7 10:36:29 2015 +0300
Committer: vozerov-gridgain <voze...@gridgain.com>
Committed: Wed Oct 7 10:36:29 2015 +0300

----------------------------------------------------------------------
 .../Apache.Ignite.Core.csproj                   |   5 +
 .../Impl/Portable/IPortableTypeDescriptor.cs    |  15 +
 .../Metadata/Opto/PortableTypeStructure.cs      | 317 +++++++++++++++++++
 .../Metadata/Opto/PortableTypeStructureEntry.cs | 127 ++++++++
 .../Opto/PortableTypeStructureJumpTable.cs      | 115 +++++++
 .../Opto/PortableTypeStructureUpdate.cs         |  84 +++++
 .../Impl/Portable/PortableFullTypeDescriptor.cs |  21 ++
 .../Portable/PortableSurrogateTypeDescriptor.cs |  24 +-
 .../Impl/Portable/PortableUtils.cs              |   4 +
 .../Impl/Portable/PortableWriterImpl.cs         |  61 +++-
 10 files changed, 770 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/9da45ed3/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..d412181 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -250,6 +250,10 @@
     <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" />
@@ -367,6 +371,7 @@
       <Link>resources\release\x86\ignite.common.dll</Link>
     </EmbeddedResource>
   </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets 
below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

http://git-wip-us.apache.org/repos/asf/ignite/blob/9da45ed3/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..389238c 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,8 @@
 namespace Apache.Ignite.Core.Impl.Portable
 {
     using System;
+    using System.Collections.Generic;
+    using Apache.Ignite.Core.Impl.Portable.Metadata.Opto;
     using Apache.Ignite.Core.Portable;
 
     /// <summary>
@@ -104,5 +106,18 @@ namespace Apache.Ignite.Core.Impl.Portable
         {
             get;
         }
+
+        /// <summary>
+        /// Type structure.
+        /// </summary>
+        PortableTypeStructure 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 UpdateStrcuture(PortableTypeStructure exp, int pathIdx, 
IList<PortableTypeStructureUpdate> updates);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/9da45ed3/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
new file mode 100644
index 0000000..cdc0859
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructure.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.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/9da45ed3/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
new file mode 100644
index 0000000..e939d56
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureEntry.cs
@@ -0,0 +1,127 @@
+/*
+ * 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/9da45ed3/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
new file mode 100644
index 0000000..b2409db
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureJumpTable.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.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/9da45ed3/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
new file mode 100644
index 0000000..56e0c12
--- /dev/null
+++ 
b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Metadata/Opto/PortableTypeStructureUpdate.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.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/9da45ed3/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..d1a714b 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,8 @@
 namespace Apache.Ignite.Core.Impl.Portable
 {
     using System;
+    using System.Collections.Generic;
+    using Apache.Ignite.Core.Impl.Portable.Metadata.Opto;
     using Apache.Ignite.Core.Portable;
 
     /// <summary>
@@ -55,6 +57,9 @@ namespace Apache.Ignite.Core.Impl.Portable
         /** Affinity field key name. */
         private readonly string _affKeyFieldName;
 
+        /** Type structure. */
+        private volatile PortableTypeStructure _typeStruct = 
PortableTypeStructure.CreateEmpty();
+
         /// <summary>
         /// Constructor.
         /// </summary>
@@ -171,5 +176,21 @@ namespace Apache.Ignite.Core.Impl.Portable
         {
             get { return _affKeyFieldName; }
         }
+
+        /** <inheritDoc /> */
+        public PortableTypeStructure TypeStructure
+        {
+            get { return _typeStruct; }
+        }
+
+        /** <inheritDoc /> */
+        public void UpdateStrcuture(PortableTypeStructure exp, int pathIdx, 
+            IList<PortableTypeStructureUpdate> updates)
+        {
+            lock (this)
+            {
+                _typeStruct = _typeStruct.Merge(exp, pathIdx, updates);
+            }
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/9da45ed3/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..adbb6bb 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,13 @@
 namespace Apache.Ignite.Core.Impl.Portable
 {
     using System;
+    using System.Collections.Generic;
+    using Apache.Ignite.Core.Impl.Portable.Metadata.Opto;
     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 +37,9 @@ namespace Apache.Ignite.Core.Impl.Portable
         /** Type name. */
         private readonly string _name;
 
+        /** Type structure. */
+        private volatile PortableTypeStructure _typeStruct = 
PortableTypeStructure.CreateEmpty();
+
         /// <summary>
         /// Constructor.
         /// </summary>
@@ -117,5 +123,21 @@ namespace Apache.Ignite.Core.Impl.Portable
         {
             get { return null; }
         }
+
+        /** <inheritDoc /> */
+        public PortableTypeStructure TypeStructure
+        {
+            get { return _typeStruct; }
+        }
+
+        /** <inheritDoc /> */
+        public void UpdateStrcuture(PortableTypeStructure exp, int pathIdx,
+            IList<PortableTypeStructureUpdate> updates)
+        {
+            lock (this)
+            {
+                _typeStruct = _typeStruct.Merge(exp, pathIdx, updates);
+            }
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/9da45ed3/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/9da45ed3/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..6a0917a 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.Metadata.Opto;
     using Apache.Ignite.Core.Portable;
 
     using PU = PortableUtils;
@@ -62,6 +63,12 @@ namespace Apache.Ignite.Core.Impl.Portable
         
         /** Current raw position. */
         private long _curRawPos;
+
+        private PortableTypeStructure _curTypeStruct;
+        private int _curPathIdx;
+        private int _curActionIdx;
+        private bool _curNewStruct;
+        private List<PortableTypeStructureUpdate> _curUpdates; 
         
         /** Whether we are currently detaching an object. */
         private bool _detaching;
@@ -1303,6 +1310,12 @@ namespace Apache.Ignite.Core.Impl.Portable
                 IPortableIdMapper oldMapper = _curMapper;
                 IPortableMetadataHandler oldMetaHnd = _curMetaHnd;
                 long oldRawPos = _curRawPos;
+                
+                PortableTypeStructure oldTypeStruct = _curTypeStruct;
+                int oldPathIdx = _curPathIdx;
+                int oldActionIdx = _curActionIdx;
+                bool oldNewStruct = _curNewStruct;
+                var oldUpdates = _curUpdates;
 
                 // Push new frame.
                 _curTypeId = desc.TypeId;
@@ -1311,6 +1324,12 @@ namespace Apache.Ignite.Core.Impl.Portable
                 _curMetaHnd = desc.MetadataEnabled ? 
_marsh.MetadataHandler(desc) : null;
                 _curRawPos = 0;
 
+                _curTypeStruct = desc.TypeStructure;
+                _curPathIdx = 0;
+                _curActionIdx = 0;
+                _curNewStruct = false;
+                _curUpdates = null;
+
                 // Write object fields.
                 desc.Serializer.WritePortable(obj, this);
 
@@ -1333,12 +1352,22 @@ namespace Apache.Ignite.Core.Impl.Portable
                         SaveMetadata(_curTypeId, desc.TypeName, 
desc.AffinityKeyFieldName, meta);
                 }
 
+                // 14. Apply structure updates 
+                if (_curUpdates != null)
+                    desc.UpdateStrcuture(_curTypeStruct, _curPathIdx, 
_curUpdates);
+                
                 // Restore old frame.
                 _curTypeId = oldTypeId;
                 _curConverter = oldConverter;
                 _curMapper = oldMapper;
                 _curMetaHnd = oldMetaHnd;
                 _curRawPos = oldRawPos;
+
+                _curTypeStruct = oldTypeStruct;
+                _curPathIdx = oldPathIdx;
+                _curActionIdx = oldActionIdx;
+                _curNewStruct = oldNewStruct;
+                _curUpdates = oldUpdates;
             }
             else
             {
@@ -1595,10 +1624,38 @@ 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 actionIdx = _curActionIdx++;
 
-            _stream.WriteInt(fieldId);
+            int fieldId;
+
+            if (!_curNewStruct)
+            {
+                fieldId = _curTypeStruct.GetFieldId(fieldName, fieldTypeId, 
ref _curPathIdx, actionIdx);
+
+                if (fieldId == 0)
+                {
+                    _curNewStruct = true;
+
+                    fieldId = PU.FieldId(_curTypeId, fieldName, _curConverter, 
_curMapper);
+
+                    if (_curUpdates == null)
+                        _curUpdates = new List<PortableTypeStructureUpdate>();
 
+                    _curUpdates.Add(new PortableTypeStructureUpdate(fieldName, 
fieldId, fieldTypeId, actionIdx));
+                }
+            }
+            else
+            {
+                fieldId = PU.FieldId(_curTypeId, fieldName, _curConverter, 
_curMapper);
+
+                if (_curUpdates == null)
+                    _curUpdates = new List<PortableTypeStructureUpdate>();
+
+                _curUpdates.Add(new PortableTypeStructureUpdate(fieldName, 
fieldId, fieldTypeId, actionIdx));
+            }
+
+            _stream.WriteInt(fieldId);
+            
             if (_curMetaHnd != null)
                 _curMetaHnd.OnFieldWrite(fieldId, fieldName, fieldTypeId);
         }

Reply via email to