Repository: ignite Updated Branches: refs/heads/master fbba10219 -> ead2b1f5a
IGNITE-3097 .NET: Improve reflective serialization performance This closes #698 Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/ead2b1f5 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/ead2b1f5 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/ead2b1f5 Branch: refs/heads/master Commit: ead2b1f5aa0637e82b55a8071dd9af911f25f2a3 Parents: fbba102 Author: Pavel Tupitsyn <ptupit...@apache.org> Authored: Fri Jun 17 19:29:33 2016 +0300 Committer: Pavel Tupitsyn <ptupit...@apache.org> Committed: Fri Jun 17 19:29:33 2016 +0300 ---------------------------------------------------------------------- .../IgniteOutputCacheProvider.cs | 6 +- .../Apache.Ignite.Core.csproj | 6 +- .../Binary/BinaryReflectiveSerializer.cs | 193 ++----------------- .../Binary/UserSerializerProxy.cs | 68 +++++++ .../Impl/Binary/BinarizableSerializer.cs | 22 ++- .../Impl/Binary/BinaryFullTypeDescriptor.cs | 8 +- .../Impl/Binary/BinaryReader.cs | 38 +--- .../BinaryReflectiveSerializerInternal.cs | 169 ++++++++++++++++ .../Binary/BinarySurrogateTypeDescriptor.cs | 4 +- .../Impl/Binary/BinarySystemTypeSerializer.cs | 20 +- .../Impl/Binary/BinaryWriter.cs | 2 +- .../Impl/Binary/DateTimeSerializer.cs | 48 +++++ .../Impl/Binary/IBinarySerializerInternal.cs | 42 ++++ .../Impl/Binary/IBinarySystemTypeSerializer.cs | 34 ---- .../Impl/Binary/IBinaryTypeDescriptor.cs | 2 +- .../Impl/Binary/Marshaller.cs | 77 ++++---- .../Impl/Binary/SerializableSerializer.cs | 48 +++++ 17 files changed, 480 insertions(+), 307 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs b/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs index d6006e5..fb7a15d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs +++ b/modules/platforms/dotnet/Apache.Ignite.AspNet/IgniteOutputCacheProvider.cs @@ -190,10 +190,10 @@ namespace Apache.Ignite.AspNet return Cache; // Round up to seconds ([OutputCache] duration is in seconds). - var expirySeconds = (long) (utcExpiry - DateTime.UtcNow).TotalSeconds; + var expirySeconds = (long) Math.Round((utcExpiry - DateTime.UtcNow).TotalSeconds); - if (expirySeconds < 1) - expirySeconds = 1; + if (expirySeconds < 0) + expirySeconds = 0; ICache<string, object> expiryCache; http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/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 4cfd4e5..34c5ddb 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj @@ -84,7 +84,10 @@ </ItemGroup> <ItemGroup> <Compile Include="Binary\BinaryReflectiveSerializer.cs" /> + <Compile Include="Impl\Binary\BinaryReflectiveSerializerInternal.cs" /> + <Compile Include="Impl\Binary\IBinarySerializerInternal.cs" /> <Compile Include="Binary\Package-Info.cs" /> + <Compile Include="Binary\UserSerializerProxy.cs" /> <Compile Include="Cache\Affinity\AffinityFunctionBase.cs" /> <Compile Include="Cache\Affinity\AffinityKey.cs" /> <Compile Include="Cache\Affinity\AffinityKeyMappedAttribute.cs" /> @@ -156,6 +159,8 @@ <Compile Include="Common\IgniteFutureCancelledException.cs" /> <Compile Include="Common\IgniteGuid.cs" /> <Compile Include="Common\Package-Info.cs" /> + <Compile Include="Impl\Binary\DateTimeSerializer.cs" /> + <Compile Include="Impl\Binary\SerializableSerializer.cs" /> <Compile Include="Impl\Binary\BinaryWriterExtensions.cs" /> <Compile Include="Impl\Common\Platform.cs" /> <Compile Include="Impl\Cache\Event\JavaCacheEntryEventFilter.cs" /> @@ -348,7 +353,6 @@ <Compile Include="Impl\Binary\IO\BinaryStreamBase.cs" /> <Compile Include="Impl\Binary\IO\BinaryHeapStream.cs" /> <Compile Include="Impl\Binary\IO\BinaryStreamAdapter.cs" /> - <Compile Include="Impl\Binary\IBinarySystemTypeSerializer.cs" /> <Compile Include="Impl\Binary\IBinaryTypeDescriptor.cs" /> <Compile Include="Impl\Binary\IBinaryWriteAware.cs" /> <Compile Include="Impl\Binary\Metadata\IBinaryTypeHandler.cs" /> http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs index 02703be..18e8c2d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/BinaryReflectiveSerializer.cs @@ -18,8 +18,6 @@ namespace Apache.Ignite.Core.Binary { using System; - using System.Collections.Generic; - using System.Reflection; using Apache.Ignite.Core.Impl.Binary; /// <summary> @@ -41,16 +39,24 @@ namespace Apache.Ignite.Core.Binary /// </summary> public sealed class BinaryReflectiveSerializer : IBinarySerializer { - /** Cached binding flags. */ - private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | - BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - /** Cached type descriptors. */ - private readonly IDictionary<Type, Descriptor> _types = new Dictionary<Type, Descriptor>(); - /** Raw mode flag. */ private bool _rawMode; + /** In use flag. */ + private bool _isInUse; + + /** <inheritdoc /> */ + public void WriteBinary(object obj, IBinaryWriter writer) + { + throw new NotSupportedException(GetType() + ".WriteBinary should not be called directly."); + } + + /** <inheritdoc /> */ + public void ReadBinary(object obj, IBinaryReader reader) + { + throw new NotSupportedException(GetType() + ".ReadBinary should not be called directly."); + } + /// <summary> /// Gets or value indicating whether raw mode serialization should be used. /// <para /> @@ -62,8 +68,8 @@ namespace Apache.Ignite.Core.Binary get { return _rawMode; } set { - if (_types.Count > 0) - throw new InvalidOperationException(typeof (BinarizableSerializer).Name + + if (_isInUse) + throw new InvalidOperationException(typeof(BinaryReflectiveSerializer).Name + ".RawMode cannot be changed after first serialization."); _rawMode = value; @@ -71,171 +77,14 @@ namespace Apache.Ignite.Core.Binary } /// <summary> - /// Write portalbe object. - /// </summary> - /// <param name="obj">Object.</param> - /// <param name="writer">Writer.</param> - /// <exception cref="BinaryObjectException">Type is not registered in serializer: + type.Name</exception> - public void WriteBinary(object obj, IBinaryWriter writer) - { - var binarizable = obj as IBinarizable; - - if (binarizable != null) - binarizable.WriteBinary(writer); - else - GetDescriptor(obj).Write(obj, writer); - } - - /// <summary> - /// Read binary object. + /// Registers the specified type. /// </summary> - /// <param name="obj">Instantiated empty object.</param> - /// <param name="reader">Reader.</param> - /// <exception cref="BinaryObjectException">Type is not registered in serializer: + type.Name</exception> - public void ReadBinary(object obj, IBinaryReader reader) - { - var binarizable = obj as IBinarizable; - - if (binarizable != null) - binarizable.ReadBinary(reader); - else - GetDescriptor(obj).Read(obj, reader); - } - - /// <summary>Register type.</summary> - /// <param name="type">Type.</param> - /// <param name="typeId">Type ID.</param> - /// <param name="converter">Name converter.</param> - /// <param name="idMapper">ID mapper.</param> - internal void Register(Type type, int typeId, IBinaryNameMapper converter, + internal IBinarySerializerInternal Register(Type type, int typeId, IBinaryNameMapper converter, IBinaryIdMapper idMapper) { - if (type.GetInterface(typeof(IBinarizable).Name) != null) - return; - - List<FieldInfo> fields = new List<FieldInfo>(); - - Type curType = type; - - while (curType != null) - { - foreach (FieldInfo field in curType.GetFields(Flags)) - { - if (!field.IsNotSerialized) - fields.Add(field); - } - - curType = curType.BaseType; - } - - IDictionary<int, string> idMap = new Dictionary<int, string>(); - - foreach (FieldInfo field in fields) - { - string fieldName = BinaryUtils.CleanFieldName(field.Name); - - int fieldId = BinaryUtils.FieldId(typeId, fieldName, converter, idMapper); - - if (idMap.ContainsKey(fieldId)) - { - throw new BinaryObjectException("Conflicting field IDs [type=" + - type.Name + ", field1=" + idMap[fieldId] + ", field2=" + fieldName + - ", fieldId=" + fieldId + ']'); - } - - idMap[fieldId] = fieldName; - } - - fields.Sort(Compare); - - Descriptor desc = new Descriptor(fields, _rawMode); - - _types[type] = desc; - } - - /// <summary> - /// Gets the descriptor for an object. - /// </summary> - private Descriptor GetDescriptor(object obj) - { - var type = obj.GetType(); - - Descriptor desc; - - if (!_types.TryGetValue(type, out desc)) - throw new BinaryObjectException("Type is not registered in serializer: " + type.Name); - - return desc; - } - - /// <summary> - /// Compare two FieldInfo instances. - /// </summary> - private static int Compare(FieldInfo info1, FieldInfo info2) { - string name1 = BinaryUtils.CleanFieldName(info1.Name); - string name2 = BinaryUtils.CleanFieldName(info2.Name); - - return string.Compare(name1, name2, StringComparison.OrdinalIgnoreCase); - } - - /// <summary> - /// Type descriptor. - /// </summary> - private class Descriptor - { - /** Write actions to be performed. */ - private readonly List<BinaryReflectiveWriteAction> _wActions; + _isInUse = true; - /** Read actions to be performed. */ - private readonly List<BinaryReflectiveReadAction> _rActions; - - /// <summary> - /// Constructor. - /// </summary> - /// <param name="fields">Fields.</param> - /// <param name="raw">Raw mode.</param> - public Descriptor(List<FieldInfo> fields, bool raw) - { - _wActions = new List<BinaryReflectiveWriteAction>(fields.Count); - _rActions = new List<BinaryReflectiveReadAction>(fields.Count); - - foreach (FieldInfo field in fields) - { - BinaryReflectiveWriteAction writeAction; - BinaryReflectiveReadAction readAction; - - BinaryReflectiveActions.GetTypeActions(field, out writeAction, out readAction, raw); - - _wActions.Add(writeAction); - _rActions.Add(readAction); - } - } - - /// <summary> - /// Write object. - /// </summary> - /// <param name="obj">Object.</param> - /// <param name="writer">Writer.</param> - public void Write(object obj, IBinaryWriter writer) - { - int cnt = _wActions.Count; - - for (int i = 0; i < cnt; i++) - _wActions[i](obj, writer); - } - - /// <summary> - /// Read object. - /// </summary> - /// <param name="obj">Object.</param> - /// <param name="reader">Reader.</param> - public void Read(object obj, IBinaryReader reader) - { - int cnt = _rActions.Count; - - for (int i = 0; i < cnt; i++ ) - _rActions[i](obj, reader); - } + return new BinaryReflectiveSerializerInternal(_rawMode).Register(type, typeId, converter, idMapper); } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Binary/UserSerializerProxy.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Binary/UserSerializerProxy.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/UserSerializerProxy.cs new file mode 100644 index 0000000..2cfc95e --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Binary/UserSerializerProxy.cs @@ -0,0 +1,68 @@ +/* + * 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.Binary +{ + using System; + using System.Diagnostics; + using System.Runtime.Serialization; + using Apache.Ignite.Core.Impl.Binary; + + /// <summary> + /// User-defined serializer wrapper. + /// </summary> + internal class UserSerializerProxy : IBinarySerializerInternal + { + /** */ + private readonly IBinarySerializer _serializer; + + /// <summary> + /// Initializes a new instance of the <see cref="UserSerializerProxy"/> class. + /// </summary> + /// <param name="serializer">The serializer.</param> + public UserSerializerProxy(IBinarySerializer serializer) + { + Debug.Assert(serializer != null); + + _serializer = serializer; + } + + /** <inheritdoc /> */ + public void WriteBinary<T>(T obj, BinaryWriter writer) + { + _serializer.WriteBinary(obj, writer); + } + + /** <inheritdoc /> */ + public T ReadBinary<T>(BinaryReader reader, Type type, int pos) + { + var obj = FormatterServices.GetUninitializedObject(type); + + reader.AddHandle(pos, obj); + + _serializer.ReadBinary(obj, reader); + + return (T) obj; + } + + /** <inheritdoc /> */ + public bool SupportsHandles + { + get { return true; } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs index aa6144b..dcb261f 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarizableSerializer.cs @@ -17,13 +17,15 @@ namespace Apache.Ignite.Core.Impl.Binary { + using System; + using System.Runtime.Serialization; using Apache.Ignite.Core.Binary; /// <summary> /// Binary serializer which only supports <see cref="IBinarizable"/> types with a default ctor. /// Does not use reflection. /// </summary> - internal class BinarizableSerializer : IBinarySerializer + internal class BinarizableSerializer : IBinarySerializerInternal { /// <summary> /// Default instance. @@ -31,15 +33,27 @@ namespace Apache.Ignite.Core.Impl.Binary public static readonly BinarizableSerializer Instance = new BinarizableSerializer(); /** <inheritdoc /> */ - public void WriteBinary(object obj, IBinaryWriter writer) + public void WriteBinary<T>(T obj, BinaryWriter writer) { - ((IBinarizable)obj).WriteBinary(writer); + ((IBinarizable) obj).WriteBinary(writer); } /** <inheritdoc /> */ - public void ReadBinary(object obj, IBinaryReader reader) + public T ReadBinary<T>(BinaryReader reader, Type type, int pos) { + var obj = (T) FormatterServices.GetUninitializedObject(type); + + reader.AddHandle(pos, obj); + ((IBinarizable)obj).ReadBinary(reader); + + return obj; + } + + /** <inheritdoc /> */ + public bool SupportsHandles + { + get { return true; } } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs index ac86770..6dc5d4d 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryFullTypeDescriptor.cs @@ -46,7 +46,7 @@ namespace Apache.Ignite.Core.Impl.Binary private readonly IBinaryIdMapper _idMapper; /** Serializer. */ - private readonly IBinarySerializer _serializer; + private readonly IBinarySerializerInternal _serializer; /** Whether to cache deserialized value in IBinaryObject */ private readonly bool _keepDeserialized; @@ -85,8 +85,8 @@ namespace Apache.Ignite.Core.Impl.Binary string typeName, bool userType, IBinaryNameMapper nameMapper, - IBinaryIdMapper idMapper, - IBinarySerializer serializer, + IBinaryIdMapper idMapper, + IBinarySerializerInternal serializer, bool keepDeserialized, string affKeyFieldName, bool isEnum) @@ -162,7 +162,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Serializer. /// </summary> - public IBinarySerializer Serializer + public IBinarySerializerInternal Serializer { get { return _serializer; } } http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs index 1403410..3ca7ec6 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs @@ -22,7 +22,6 @@ namespace Apache.Ignite.Core.Impl.Binary using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; - using System.Runtime.Serialization; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Impl.Binary.IO; using Apache.Ignite.Core.Impl.Binary.Structure; @@ -725,29 +724,7 @@ namespace Apache.Ignite.Core.Impl.Binary // Read object. Stream.Seek(pos + BinaryObjectHeader.Size, SeekOrigin.Begin); - object obj; - - var sysSerializer = desc.Serializer as IBinarySystemTypeSerializer; - - if (sysSerializer != null) - obj = sysSerializer.ReadInstance(this); - else - { - try - { - obj = FormatterServices.GetUninitializedObject(desc.Type); - - // Save handle. - AddHandle(pos, obj); - } - catch (Exception e) - { - throw new BinaryObjectException("Failed to create type instance: " + - desc.Type.AssemblyQualifiedName, e); - } - - desc.Serializer.ReadBinary(obj, this); - } + var obj = desc.Serializer.ReadBinary<T>(this, desc.Type, pos); _curStruct.UpdateReaderStructure(); @@ -759,18 +736,7 @@ namespace Apache.Ignite.Core.Impl.Binary _curSchema = oldSchema; _curSchemaMap = oldSchemaMap; - // Process wrappers. We could introduce a common interface, but for only 2 if-else is faster. - var wrappedSerializable = obj as SerializableObjectHolder; - - if (wrappedSerializable != null) - return (T) wrappedSerializable.Item; - - var wrappedDateTime = obj as DateTimeHolder; - - if (wrappedDateTime != null) - return TypeCaster<T>.Cast(wrappedDateTime.Item); - - return (T) obj; + return obj; } } finally http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializerInternal.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializerInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializerInternal.cs new file mode 100644 index 0000000..c9fd3cc --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializerInternal.cs @@ -0,0 +1,169 @@ +/* + * 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.Binary +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; + using System.Runtime.Serialization; + using Apache.Ignite.Core.Binary; + + /// <summary> + /// Internal reflective serializer. + /// </summary> + internal sealed class BinaryReflectiveSerializerInternal : IBinarySerializerInternal + { + /** Cached binding flags. */ + private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | + BindingFlags.NonPublic | BindingFlags.DeclaredOnly; + + /** Raw mode flag. */ + private readonly bool _rawMode; + + /** Write actions to be performed. */ + private readonly BinaryReflectiveWriteAction[] _wActions; + + /** Read actions to be performed. */ + private readonly BinaryReflectiveReadAction[] _rActions; + + /// <summary> + /// Initializes a new instance of the <see cref="BinaryReflectiveSerializer"/> class. + /// </summary> + public BinaryReflectiveSerializerInternal(bool raw) + { + _rawMode = raw; + } + + /// <summary> + /// Initializes a new instance of the <see cref="BinaryReflectiveSerializer"/> class. + /// </summary> + private BinaryReflectiveSerializerInternal(BinaryReflectiveWriteAction[] wActions, BinaryReflectiveReadAction[] rActions, bool raw) + { + Debug.Assert(wActions != null); + Debug.Assert(rActions != null); + + _wActions = wActions; + _rActions = rActions; + _rawMode = raw; + } + + /** <inheritdoc /> */ + void IBinarySerializerInternal.WriteBinary<T>(T obj, BinaryWriter writer) + { + Debug.Assert(_wActions != null); + + foreach (var action in _wActions) + action(obj, writer); + } + + /** <inheritdoc /> */ + T IBinarySerializerInternal.ReadBinary<T>(BinaryReader reader, Type type, int pos) + { + Debug.Assert(_rActions != null); + + var obj = FormatterServices.GetUninitializedObject(type); + + reader.AddHandle(pos, obj); + + foreach (var action in _rActions) + action(obj, reader); + + return (T) obj; + } + + /** <inheritdoc /> */ + bool IBinarySerializerInternal.SupportsHandles + { + get { return true; } + } + + /// <summary>Register type.</summary> + /// <param name="type">Type.</param> + /// <param name="typeId">Type ID.</param> + /// <param name="converter">Name converter.</param> + /// <param name="idMapper">ID mapper.</param> + internal BinaryReflectiveSerializerInternal Register(Type type, int typeId, IBinaryNameMapper converter, + IBinaryIdMapper idMapper) + { + Debug.Assert(_wActions == null && _rActions == null); + + List<FieldInfo> fields = new List<FieldInfo>(); + + Type curType = type; + + while (curType != null) + { + foreach (FieldInfo field in curType.GetFields(Flags)) + { + if (!field.IsNotSerialized) + fields.Add(field); + } + + curType = curType.BaseType; + } + + IDictionary<int, string> idMap = new Dictionary<int, string>(); + + foreach (FieldInfo field in fields) + { + string fieldName = BinaryUtils.CleanFieldName(field.Name); + + int fieldId = BinaryUtils.FieldId(typeId, fieldName, converter, idMapper); + + if (idMap.ContainsKey(fieldId)) + { + throw new BinaryObjectException("Conflicting field IDs [type=" + + type.Name + ", field1=" + idMap[fieldId] + ", field2=" + fieldName + + ", fieldId=" + fieldId + ']'); + } + + idMap[fieldId] = fieldName; + } + + fields.Sort(Compare); + + var wActions = new BinaryReflectiveWriteAction[fields.Count]; + var rActions = new BinaryReflectiveReadAction[fields.Count]; + + for (int i = 0; i < fields.Count; i++) + { + BinaryReflectiveWriteAction writeAction; + BinaryReflectiveReadAction readAction; + + BinaryReflectiveActions.GetTypeActions(fields[i], out writeAction, out readAction, _rawMode); + + wActions[i] = writeAction; + rActions[i] = readAction; + } + + return new BinaryReflectiveSerializerInternal(wActions, rActions, _rawMode); + } + + /// <summary> + /// Compare two FieldInfo instances. + /// </summary> + private static int Compare(FieldInfo info1, FieldInfo info2) + { + string name1 = BinaryUtils.CleanFieldName(info1.Name); + string name2 = BinaryUtils.CleanFieldName(info2.Name); + + return string.Compare(name1, name2, StringComparison.OrdinalIgnoreCase); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs index 04028b5..16e3032 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs @@ -113,9 +113,9 @@ namespace Apache.Ignite.Core.Impl.Binary } /** <inheritDoc /> */ - public IBinarySerializer Serializer + public IBinarySerializerInternal Serializer { - get { return _cfg.DefaultSerializer; } + get { return new UserSerializerProxy(_cfg.DefaultSerializer); } } /** <inheritDoc /> */ http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs index cc145e9..b416848 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemTypeSerializer.cs @@ -19,13 +19,13 @@ namespace Apache.Ignite.Core.Impl.Binary { using System; using System.Diagnostics; - using Apache.Ignite.Core.Binary; + using Apache.Ignite.Core.Impl.Common; /// <summary> /// Binary serializer for system types. /// </summary> /// <typeparam name="T">Object type.</typeparam> - internal class BinarySystemTypeSerializer<T> : IBinarySystemTypeSerializer where T : IBinaryWriteAware + internal class BinarySystemTypeSerializer<T> : IBinarySerializerInternal where T : IBinaryWriteAware { /** Ctor delegate. */ private readonly Func<BinaryReader, T> _ctor; @@ -41,22 +41,22 @@ namespace Apache.Ignite.Core.Impl.Binary _ctor = ctor; } - /** <inheritdoc /> */ - public void WriteBinary(object obj, IBinaryWriter writer) + /** <inheritDoc /> */ + public void WriteBinary<T1>(T1 obj, BinaryWriter writer) { - ((T) obj).WriteBinary(writer); + TypeCaster<T>.Cast(obj).WriteBinary(writer); } - /** <inheritdoc /> */ - public void ReadBinary(object obj, IBinaryReader reader) + /** <inheritDoc /> */ + public T1 ReadBinary<T1>(BinaryReader reader, Type type, int pos) { - throw new NotSupportedException("System serializer does not support ReadBinary."); + return TypeCaster<T1>.Cast(_ctor(reader)); } /** <inheritdoc /> */ - public object ReadInstance(BinaryReader reader) + public bool SupportsHandles { - return _ctor(reader); + get { return false; } } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs index 1ac98c4..fd5b2ee 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryWriter.cs @@ -1101,7 +1101,7 @@ namespace Apache.Ignite.Core.Impl.Binary var pos = _stream.Position; // Dealing with handles. - if (!(desc.Serializer is IBinarySystemTypeSerializer) && WriteHandle(pos, obj)) + if (desc.Serializer.SupportsHandles && WriteHandle(pos, obj)) return; // Skip header length as not everything is known now http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/DateTimeSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/DateTimeSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/DateTimeSerializer.cs new file mode 100644 index 0000000..bea7d58 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/DateTimeSerializer.cs @@ -0,0 +1,48 @@ +/* + * 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.Binary +{ + using System; + using Apache.Ignite.Core.Impl.Common; + + /// <summary> + /// DateTime serializer. + /// </summary> + internal class DateTimeSerializer : IBinarySerializerInternal + { + /** <inheritdoc /> */ + public void WriteBinary<T>(T obj, BinaryWriter writer) + { + TypeCaster<DateTimeHolder>.Cast(obj).WriteBinary(writer); + } + + /** <inheritdoc /> */ + public T ReadBinary<T>(BinaryReader reader, Type type, int pos) + { + var holder = new DateTimeHolder(reader); + + return TypeCaster<T>.Cast(holder.Item); + } + + /** <inheritdoc /> */ + public bool SupportsHandles + { + get { return false; } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySerializerInternal.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySerializerInternal.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySerializerInternal.cs new file mode 100644 index 0000000..58844d6 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySerializerInternal.cs @@ -0,0 +1,42 @@ +/* + * 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.Binary +{ + using System; + + /// <summary> + /// Internal generic serializer interface. + /// </summary> + internal interface IBinarySerializerInternal + { + /// <summary> + /// Write binary object. + /// </summary> + void WriteBinary<T>(T obj, BinaryWriter writer); + + /// <summary> + /// Read binary object. + /// </summary> + T ReadBinary<T>(BinaryReader reader, Type type, int pos); + + /// <summary> + /// Gets a value indicating whether this serializer supports handles. + /// </summary> + bool SupportsHandles { get; } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySystemTypeSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySystemTypeSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySystemTypeSerializer.cs deleted file mode 100644 index 3571ffb..0000000 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinarySystemTypeSerializer.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Apache.Ignite.Core.Impl.Binary -{ - using Apache.Ignite.Core.Binary; - - /// <summary> - /// Serializer for system types that can create instances directly from a stream and does not support handles. - /// </summary> - internal interface IBinarySystemTypeSerializer : IBinarySerializer - { - /// <summary> - /// Reads the instance from a reader. - /// </summary> - /// <param name="reader">The reader.</param> - /// <returns>Deserialized instance.</returns> - object ReadInstance(BinaryReader reader); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs index e50279c..e25d720 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/IBinaryTypeDescriptor.cs @@ -65,7 +65,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Serializer. /// </summary> - IBinarySerializer Serializer { get; } + IBinarySerializerInternal Serializer { get; } /// <summary> /// Affinity key field name. http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs index aa881bb..5836b48 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs @@ -82,24 +82,19 @@ namespace Apache.Ignite.Core.Impl.Binary AddSystemTypes(); // 2. Define user types. - var dfltSerializer = cfg.DefaultSerializer == null ? new BinaryReflectiveSerializer() : null; - var typeResolver = new TypeResolver(); ICollection<BinaryTypeConfiguration> typeCfgs = cfg.TypeConfigurations; if (typeCfgs != null) foreach (BinaryTypeConfiguration typeCfg in typeCfgs) - AddUserType(cfg, typeCfg, typeResolver, dfltSerializer); + AddUserType(cfg, typeCfg, typeResolver); var typeNames = cfg.Types; if (typeNames != null) foreach (string typeName in typeNames) - AddUserType(cfg, new BinaryTypeConfiguration(typeName), typeResolver, dfltSerializer); - - if (cfg.DefaultSerializer == null) - cfg.DefaultSerializer = dfltSerializer; + AddUserType(cfg, new BinaryTypeConfiguration(typeName), typeResolver); _cfg = cfg; } @@ -406,9 +401,8 @@ namespace Apache.Ignite.Core.Impl.Binary /// <param name="cfg">Configuration.</param> /// <param name="typeCfg">Type configuration.</param> /// <param name="typeResolver">The type resolver.</param> - /// <param name="dfltSerializer">The default serializer.</param> private void AddUserType(BinaryConfiguration cfg, BinaryTypeConfiguration typeCfg, - TypeResolver typeResolver, IBinarySerializer dfltSerializer) + TypeResolver typeResolver) { // Get converter/mapper/serializer. IBinaryNameMapper nameMapper = typeCfg.NameMapper ?? cfg.DefaultNameMapper; @@ -422,19 +416,6 @@ namespace Apache.Ignite.Core.Impl.Binary if (type != null) { - // Type is found. - var typeName = BinaryUtils.GetTypeName(type); - - int typeId = BinaryUtils.TypeId(typeName, nameMapper, idMapper); - - var serializer = typeCfg.Serializer ?? cfg.DefaultSerializer - ?? GetBinarizableSerializer(type) ?? dfltSerializer; - - var refSerializer = serializer as BinaryReflectiveSerializer; - - if (refSerializer != null) - refSerializer.Register(type, typeId, nameMapper, idMapper); - if (typeCfg.IsEnum != type.IsEnum) throw new BinaryObjectException( string.Format( @@ -442,7 +423,11 @@ namespace Apache.Ignite.Core.Impl.Binary "Configuration value: IsEnum={0}, actual type: IsEnum={1}", typeCfg.IsEnum, type.IsEnum)); + // Type is found. + var typeName = BinaryUtils.GetTypeName(type); + int typeId = BinaryUtils.TypeId(typeName, nameMapper, idMapper); var affKeyFld = typeCfg.AffinityKeyFieldName ?? GetAffinityKeyFieldNameFromAttribute(type); + var serializer = GetSerializer(cfg, typeCfg, type, typeId, nameMapper, idMapper); AddType(type, typeId, typeName, true, keepDeserialized, nameMapper, idMapper, serializer, affKeyFld, type.IsEnum); @@ -460,6 +445,29 @@ namespace Apache.Ignite.Core.Impl.Binary } /// <summary> + /// Gets the serializer. + /// </summary> + private static IBinarySerializerInternal GetSerializer(BinaryConfiguration cfg, BinaryTypeConfiguration typeCfg, + Type type, int typeId, IBinaryNameMapper nameMapper, IBinaryIdMapper idMapper) + { + var serializer = typeCfg.Serializer ?? cfg.DefaultSerializer; + + if (serializer == null) + { + if (type.GetInterfaces().Contains(typeof(IBinarizable))) + return BinarizableSerializer.Instance; + + serializer = new BinaryReflectiveSerializer(); + } + + var refSerializer = serializer as BinaryReflectiveSerializer; + + return refSerializer != null + ? refSerializer.Register(type, typeId, nameMapper, idMapper) + : new UserSerializerProxy(serializer); + } + + /// <summary> /// Gets the affinity key field name from attribute. /// </summary> private static string GetAffinityKeyFieldNameFromAttribute(Type type) @@ -478,18 +486,6 @@ namespace Apache.Ignite.Core.Impl.Binary } /// <summary> - /// Gets the <see cref="BinarizableSerializer"/> for a type if it is compatible. - /// </summary> - /// <param name="type">The type.</param> - /// <returns>Resulting <see cref="BinarizableSerializer"/>, or null.</returns> - private static IBinarySerializer GetBinarizableSerializer(Type type) - { - return type.GetInterfaces().Contains(typeof (IBinarizable)) - ? BinarizableSerializer.Instance - : null; - } - - /// <summary> /// Add type. /// </summary> /// <param name="type">Type.</param> @@ -504,7 +500,7 @@ namespace Apache.Ignite.Core.Impl.Binary /// <param name="isEnum">Enum flag.</param> private void AddType(Type type, int typeId, string typeName, bool userType, bool keepDeserialized, IBinaryNameMapper nameMapper, IBinaryIdMapper idMapper, - IBinarySerializer serializer, string affKeyFieldName, bool isEnum) + IBinarySerializerInternal serializer, string affKeyFieldName, bool isEnum) { long typeKey = BinaryUtils.TypeKey(userType, typeId); @@ -540,12 +536,13 @@ namespace Apache.Ignite.Core.Impl.Binary /// <summary> /// Adds a predefined system type. /// </summary> - private void AddSystemType<T>(int typeId, Func<BinaryReader, T> ctor, string affKeyFldName = null) + private void AddSystemType<T>(int typeId, Func<BinaryReader, T> ctor, string affKeyFldName = null, + IBinarySerializerInternal serializer = null) where T : IBinaryWriteAware { var type = typeof(T); - var serializer = new BinarySystemTypeSerializer<T>(ctor); + serializer = serializer ?? new BinarySystemTypeSerializer<T>(ctor); if (typeId == 0) typeId = BinaryUtils.TypeId(type.Name, null, null); @@ -568,8 +565,10 @@ namespace Apache.Ignite.Core.Impl.Binary AddSystemType(BinaryUtils.TypeComputeFuncJob, w => new ComputeFuncJob(w)); AddSystemType(BinaryUtils.TypeComputeActionJob, w => new ComputeActionJob(w)); AddSystemType(BinaryUtils.TypeContinuousQueryRemoteFilterHolder, w => new ContinuousQueryFilterHolder(w)); - AddSystemType(BinaryUtils.TypeSerializableHolder, w => new SerializableObjectHolder(w)); - AddSystemType(BinaryUtils.TypeDateTimeHolder, w => new DateTimeHolder(w)); + AddSystemType(BinaryUtils.TypeSerializableHolder, w => new SerializableObjectHolder(w), + serializer: new SerializableSerializer()); + AddSystemType(BinaryUtils.TypeDateTimeHolder, w => new DateTimeHolder(w), + serializer: new DateTimeSerializer()); AddSystemType(BinaryUtils.TypeCacheEntryProcessorHolder, w => new CacheEntryProcessorHolder(w)); AddSystemType(BinaryUtils.TypeCacheEntryPredicateHolder, w => new CacheEntryFilterHolder(w)); AddSystemType(BinaryUtils.TypeMessageListenerHolder, w => new MessageListenerHolder(w)); http://git-wip-us.apache.org/repos/asf/ignite/blob/ead2b1f5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs new file mode 100644 index 0000000..55ac3c0 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/SerializableSerializer.cs @@ -0,0 +1,48 @@ +/* + * 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.Binary +{ + using System; + using Apache.Ignite.Core.Impl.Common; + + /// <summary> + /// Serializable serializer. + /// </summary> + internal class SerializableSerializer : IBinarySerializerInternal + { + /** <inheritdoc /> */ + public void WriteBinary<T>(T obj, BinaryWriter writer) + { + TypeCaster<SerializableObjectHolder>.Cast(obj).WriteBinary(writer); + } + + /** <inheritdoc /> */ + public T ReadBinary<T>(BinaryReader reader, Type type, int pos) + { + var holder = new SerializableObjectHolder(reader); + + return TypeCaster<T>.Cast(holder.Item); + } + + /** <inheritdoc /> */ + public bool SupportsHandles + { + get { return false; } + } + } +} \ No newline at end of file