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