http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/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 new file mode 100644 index 0000000..1dec7ba --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs @@ -0,0 +1,940 @@ +/* + * 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; + 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; + using Apache.Ignite.Core.Impl.Common; + + /// <summary> + /// Binary reader implementation. + /// </summary> + internal class BinaryReader : IBinaryReader, IBinaryRawReader + { + /** Marshaller. */ + private readonly Marshaller _marsh; + + /** Type descriptors. */ + private readonly IDictionary<long, IBinaryTypeDescriptor> _descs; + + /** Parent builder. */ + private readonly BinaryObjectBuilder _builder; + + /** Handles. */ + private BinaryReaderHandleDictionary _hnds; + + /** Current position. */ + private int _curPos; + + /** Current raw flag. */ + private bool _curRaw; + + /** Detach flag. */ + private bool _detach; + + /** Binary read mode. */ + private BinaryMode _mode; + + /** Current type structure tracker. */ + private BinaryStructureTracker _curStruct; + + /** Current schema. */ + private int[] _curSchema; + + /** Current schema with positions. */ + private Dictionary<int, int> _curSchemaMap; + + /** Current header. */ + private BinaryObjectHeader _curHdr; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="marsh">Marshaller.</param> + /// <param name="descs">Descriptors.</param> + /// <param name="stream">Input stream.</param> + /// <param name="mode">The mode.</param> + /// <param name="builder">Builder.</param> + public BinaryReader + (Marshaller marsh, + IDictionary<long, IBinaryTypeDescriptor> descs, + IBinaryStream stream, + BinaryMode mode, + BinaryObjectBuilder builder) + { + _marsh = marsh; + _descs = descs; + _mode = mode; + _builder = builder; + + Stream = stream; + } + + /// <summary> + /// Gets the marshaller. + /// </summary> + public Marshaller Marshaller + { + get { return _marsh; } + } + + /** <inheritdoc /> */ + public IBinaryRawReader GetRawReader() + { + MarkRaw(); + + return this; + } + + /** <inheritdoc /> */ + public bool ReadBoolean(string fieldName) + { + return ReadField(fieldName, r => r.ReadBoolean(), BinaryUtils.TypeBool); + } + + /** <inheritdoc /> */ + public bool ReadBoolean() + { + return Stream.ReadBool(); + } + + /** <inheritdoc /> */ + public bool[] ReadBooleanArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadBooleanArray, BinaryUtils.TypeArrayBool); + } + + /** <inheritdoc /> */ + public bool[] ReadBooleanArray() + { + return Read(BinaryUtils.ReadBooleanArray, BinaryUtils.TypeArrayBool); + } + + /** <inheritdoc /> */ + public byte ReadByte(string fieldName) + { + return ReadField(fieldName, ReadByte, BinaryUtils.TypeByte); + } + + /** <inheritdoc /> */ + public byte ReadByte() + { + return Stream.ReadByte(); + } + + /** <inheritdoc /> */ + public byte[] ReadByteArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadByteArray, BinaryUtils.TypeArrayByte); + } + + /** <inheritdoc /> */ + public byte[] ReadByteArray() + { + return Read(BinaryUtils.ReadByteArray, BinaryUtils.TypeArrayByte); + } + + /** <inheritdoc /> */ + public short ReadShort(string fieldName) + { + return ReadField(fieldName, ReadShort, BinaryUtils.TypeShort); + } + + /** <inheritdoc /> */ + public short ReadShort() + { + return Stream.ReadShort(); + } + + /** <inheritdoc /> */ + public short[] ReadShortArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadShortArray, BinaryUtils.TypeArrayShort); + } + + /** <inheritdoc /> */ + public short[] ReadShortArray() + { + return Read(BinaryUtils.ReadShortArray, BinaryUtils.TypeArrayShort); + } + + /** <inheritdoc /> */ + public char ReadChar(string fieldName) + { + return ReadField(fieldName, ReadChar, BinaryUtils.TypeChar); + } + + /** <inheritdoc /> */ + public char ReadChar() + { + return Stream.ReadChar(); + } + + /** <inheritdoc /> */ + public char[] ReadCharArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadCharArray, BinaryUtils.TypeArrayChar); + } + + /** <inheritdoc /> */ + public char[] ReadCharArray() + { + return Read(BinaryUtils.ReadCharArray, BinaryUtils.TypeArrayChar); + } + + /** <inheritdoc /> */ + public int ReadInt(string fieldName) + { + return ReadField(fieldName, ReadInt, BinaryUtils.TypeInt); + } + + /** <inheritdoc /> */ + public int ReadInt() + { + return Stream.ReadInt(); + } + + /** <inheritdoc /> */ + public int[] ReadIntArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadIntArray, BinaryUtils.TypeArrayInt); + } + + /** <inheritdoc /> */ + public int[] ReadIntArray() + { + return Read(BinaryUtils.ReadIntArray, BinaryUtils.TypeArrayInt); + } + + /** <inheritdoc /> */ + public long ReadLong(string fieldName) + { + return ReadField(fieldName, ReadLong, BinaryUtils.TypeLong); + } + + /** <inheritdoc /> */ + public long ReadLong() + { + return Stream.ReadLong(); + } + + /** <inheritdoc /> */ + public long[] ReadLongArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadLongArray, BinaryUtils.TypeArrayLong); + } + + /** <inheritdoc /> */ + public long[] ReadLongArray() + { + return Read(BinaryUtils.ReadLongArray, BinaryUtils.TypeArrayLong); + } + + /** <inheritdoc /> */ + public float ReadFloat(string fieldName) + { + return ReadField(fieldName, ReadFloat, BinaryUtils.TypeFloat); + } + + /** <inheritdoc /> */ + public float ReadFloat() + { + return Stream.ReadFloat(); + } + + /** <inheritdoc /> */ + public float[] ReadFloatArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadFloatArray, BinaryUtils.TypeArrayFloat); + } + + /** <inheritdoc /> */ + public float[] ReadFloatArray() + { + return Read(BinaryUtils.ReadFloatArray, BinaryUtils.TypeArrayFloat); + } + + /** <inheritdoc /> */ + public double ReadDouble(string fieldName) + { + return ReadField(fieldName, ReadDouble, BinaryUtils.TypeDouble); + } + + /** <inheritdoc /> */ + public double ReadDouble() + { + return Stream.ReadDouble(); + } + + /** <inheritdoc /> */ + public double[] ReadDoubleArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadDoubleArray, BinaryUtils.TypeArrayDouble); + } + + /** <inheritdoc /> */ + public double[] ReadDoubleArray() + { + return Read(BinaryUtils.ReadDoubleArray, BinaryUtils.TypeArrayDouble); + } + + /** <inheritdoc /> */ + public decimal? ReadDecimal(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadDecimal, BinaryUtils.TypeDecimal); + } + + /** <inheritdoc /> */ + public decimal? ReadDecimal() + { + return Read(BinaryUtils.ReadDecimal, BinaryUtils.TypeDecimal); + } + + /** <inheritdoc /> */ + public decimal?[] ReadDecimalArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadDecimalArray, BinaryUtils.TypeArrayDecimal); + } + + /** <inheritdoc /> */ + public decimal?[] ReadDecimalArray() + { + return Read(BinaryUtils.ReadDecimalArray, BinaryUtils.TypeArrayDecimal); + } + + /** <inheritdoc /> */ + public DateTime? ReadTimestamp(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadTimestamp, BinaryUtils.TypeTimestamp); + } + + /** <inheritdoc /> */ + public DateTime? ReadTimestamp() + { + return Read(BinaryUtils.ReadTimestamp, BinaryUtils.TypeTimestamp); + } + + /** <inheritdoc /> */ + public DateTime?[] ReadTimestampArray(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadTimestampArray, BinaryUtils.TypeArrayTimestamp); + } + + /** <inheritdoc /> */ + public DateTime?[] ReadTimestampArray() + { + return Read(BinaryUtils.ReadTimestampArray, BinaryUtils.TypeArrayTimestamp); + } + + /** <inheritdoc /> */ + public string ReadString(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadString, BinaryUtils.TypeString); + } + + /** <inheritdoc /> */ + public string ReadString() + { + return Read(BinaryUtils.ReadString, BinaryUtils.TypeString); + } + + /** <inheritdoc /> */ + public string[] ReadStringArray(string fieldName) + { + return ReadField(fieldName, r => BinaryUtils.ReadArray<string>(r, false), BinaryUtils.TypeArrayString); + } + + /** <inheritdoc /> */ + public string[] ReadStringArray() + { + return Read(r => BinaryUtils.ReadArray<string>(r, false), BinaryUtils.TypeArrayString); + } + + /** <inheritdoc /> */ + public Guid? ReadGuid(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadGuid, BinaryUtils.TypeGuid); + } + + /** <inheritdoc /> */ + public Guid? ReadGuid() + { + return Read(BinaryUtils.ReadGuid, BinaryUtils.TypeGuid); + } + + /** <inheritdoc /> */ + public Guid?[] ReadGuidArray(string fieldName) + { + return ReadField(fieldName, r => BinaryUtils.ReadArray<Guid?>(r, false), BinaryUtils.TypeArrayGuid); + } + + /** <inheritdoc /> */ + public Guid?[] ReadGuidArray() + { + return Read(r => BinaryUtils.ReadArray<Guid?>(r, false), BinaryUtils.TypeArrayGuid); + } + + /** <inheritdoc /> */ + public T ReadEnum<T>(string fieldName) + { + return ReadField(fieldName, BinaryUtils.ReadEnum<T>, BinaryUtils.TypeEnum); + } + + /** <inheritdoc /> */ + public T ReadEnum<T>() + { + return Read(BinaryUtils.ReadEnum<T>, BinaryUtils.TypeEnum); + } + + /** <inheritdoc /> */ + public T[] ReadEnumArray<T>(string fieldName) + { + return ReadField(fieldName, r => BinaryUtils.ReadArray<T>(r, true), BinaryUtils.TypeArrayEnum); + } + + /** <inheritdoc /> */ + public T[] ReadEnumArray<T>() + { + return Read(r => BinaryUtils.ReadArray<T>(r, true), BinaryUtils.TypeArrayEnum); + } + + /** <inheritdoc /> */ + public T ReadObject<T>(string fieldName) + { + if (_curRaw) + throw new BinaryObjectException("Cannot read named fields after raw data is read."); + + if (SeekField(fieldName)) + return Deserialize<T>(); + + return default(T); + } + + /** <inheritdoc /> */ + public T ReadObject<T>() + { + return Deserialize<T>(); + } + + /** <inheritdoc /> */ + public T[] ReadArray<T>(string fieldName) + { + return ReadField(fieldName, r => BinaryUtils.ReadArray<T>(r, true), BinaryUtils.TypeArray); + } + + /** <inheritdoc /> */ + public T[] ReadArray<T>() + { + return Read(r => BinaryUtils.ReadArray<T>(r, true), BinaryUtils.TypeArray); + } + + /** <inheritdoc /> */ + public ICollection ReadCollection(string fieldName) + { + return ReadCollection(fieldName, null, null); + } + + /** <inheritdoc /> */ + public ICollection ReadCollection() + { + return ReadCollection(null, null); + } + + /** <inheritdoc /> */ + public ICollection ReadCollection(string fieldName, CollectionFactory factory, + CollectionAdder adder) + { + return ReadField(fieldName, r => BinaryUtils.ReadCollection(r, factory, adder), BinaryUtils.TypeCollection); + } + + /** <inheritdoc /> */ + public ICollection ReadCollection(CollectionFactory factory, + CollectionAdder adder) + { + return Read(r => BinaryUtils.ReadCollection(r, factory, adder), BinaryUtils.TypeCollection); + } + + /** <inheritdoc /> */ + public IDictionary ReadDictionary(string fieldName) + { + return ReadDictionary(fieldName, null); + } + + /** <inheritdoc /> */ + public IDictionary ReadDictionary() + { + return ReadDictionary((DictionaryFactory)null); + } + + /** <inheritdoc /> */ + public IDictionary ReadDictionary(string fieldName, DictionaryFactory factory) + { + return ReadField(fieldName, r => BinaryUtils.ReadDictionary(r, factory), BinaryUtils.TypeDictionary); + } + + /** <inheritdoc /> */ + public IDictionary ReadDictionary(DictionaryFactory factory) + { + return Read(r => BinaryUtils.ReadDictionary(r, factory), BinaryUtils.TypeDictionary); + } + + /// <summary> + /// Enable detach mode for the next object read. + /// </summary> + public void DetachNext() + { + _detach = true; + } + + /// <summary> + /// Deserialize object. + /// </summary> + /// <returns>Deserialized object.</returns> + public T Deserialize<T>() + { + int pos = Stream.Position; + + byte hdr = Stream.ReadByte(); + + var doDetach = _detach; // save detach flag into a var and reset so it does not go deeper + + _detach = false; + + switch (hdr) + { + case BinaryUtils.HdrNull: + if (default(T) != null) + throw new BinaryObjectException(string.Format("Invalid data on deserialization. " + + "Expected: '{0}' But was: null", typeof (T))); + + return default(T); + + case BinaryUtils.HdrHnd: + return ReadHandleObject<T>(pos); + + case BinaryUtils.HdrFull: + return ReadFullObject<T>(pos); + + case BinaryUtils.TypeBinary: + return ReadBinaryObject<T>(doDetach); + } + + if (BinaryUtils.IsPredefinedType(hdr)) + return BinarySystemHandlers.ReadSystemType<T>(hdr, this); + + throw new BinaryObjectException("Invalid header on deserialization [pos=" + pos + ", hdr=" + hdr + ']'); + } + + /// <summary> + /// Reads the binary object. + /// </summary> + private T ReadBinaryObject<T>(bool doDetach) + { + var len = Stream.ReadInt(); + + var binaryBytesPos = Stream.Position; + + if (_mode != BinaryMode.Deserialize) + return TypeCaster<T>.Cast(ReadAsBinary(binaryBytesPos, len, doDetach)); + + Stream.Seek(len, SeekOrigin.Current); + + var offset = Stream.ReadInt(); + + var retPos = Stream.Position; + + Stream.Seek(binaryBytesPos + offset, SeekOrigin.Begin); + + _mode = BinaryMode.KeepBinary; + + try + { + return Deserialize<T>(); + } + finally + { + _mode = BinaryMode.Deserialize; + + Stream.Seek(retPos, SeekOrigin.Begin); + } + } + + /// <summary> + /// Reads the binary object in binary form. + /// </summary> + private BinaryObject ReadAsBinary(int binaryBytesPos, int dataLen, bool doDetach) + { + try + { + Stream.Seek(dataLen + binaryBytesPos, SeekOrigin.Begin); + + var offs = Stream.ReadInt(); // offset inside data + + var pos = binaryBytesPos + offs; + + var hdr = BinaryObjectHeader.Read(Stream, pos); + + if (!doDetach) + return new BinaryObject(_marsh, Stream.GetArray(), pos, hdr); + + Stream.Seek(pos, SeekOrigin.Begin); + + return new BinaryObject(_marsh, Stream.ReadByteArray(hdr.Length), 0, hdr); + } + finally + { + Stream.Seek(binaryBytesPos + dataLen + 4, SeekOrigin.Begin); + } + } + + /// <summary> + /// Reads the full object. + /// </summary> + [SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "hashCode")] + private T ReadFullObject<T>(int pos) + { + var hdr = BinaryObjectHeader.Read(Stream, pos); + + // Validate protocol version. + BinaryUtils.ValidateProtocolVersion(hdr.Version); + + try + { + // Already read this object? + object hndObj; + + if (_hnds != null && _hnds.TryGetValue(pos, out hndObj)) + return (T) hndObj; + + if (hdr.IsUserType && _mode == BinaryMode.ForceBinary) + { + BinaryObject portObj; + + if (_detach) + { + Stream.Seek(pos, SeekOrigin.Begin); + + portObj = new BinaryObject(_marsh, Stream.ReadByteArray(hdr.Length), 0, hdr); + } + else + portObj = new BinaryObject(_marsh, Stream.GetArray(), pos, hdr); + + T obj = _builder == null ? TypeCaster<T>.Cast(portObj) : TypeCaster<T>.Cast(_builder.Child(portObj)); + + AddHandle(pos, obj); + + return obj; + } + else + { + // Find descriptor. + IBinaryTypeDescriptor desc; + + if (!_descs.TryGetValue(BinaryUtils.TypeKey(hdr.IsUserType, hdr.TypeId), out desc)) + throw new BinaryObjectException("Unknown type ID: " + hdr.TypeId); + + // Instantiate object. + if (desc.Type == null) + throw new BinaryObjectException("No matching type found for object [typeId=" + + desc.TypeId + ", typeName=" + desc.TypeName + ']'); + + // Preserve old frame. + var oldHdr = _curHdr; + int oldPos = _curPos; + var oldStruct = _curStruct; + bool oldRaw = _curRaw; + var oldSchema = _curSchema; + var oldSchemaMap = _curSchemaMap; + + // Set new frame. + _curHdr = hdr; + _curPos = pos; + + _curSchema = desc.Schema.Get(hdr.SchemaId); + + if (_curSchema == null) + { + _curSchema = ReadSchema(); + + desc.Schema.Add(hdr.SchemaId, _curSchema); + } + + _curStruct = new BinaryStructureTracker(desc, desc.ReaderTypeStructure); + _curRaw = false; + + // 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); + } + + _curStruct.UpdateReaderStructure(); + + // Restore old frame. + _curHdr = oldHdr; + _curPos = oldPos; + _curStruct = oldStruct; + _curRaw = oldRaw; + _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; + } + } + finally + { + // Advance stream pointer. + Stream.Seek(pos + hdr.Length, SeekOrigin.Begin); + } + } + + /// <summary> + /// Reads the schema. + /// </summary> + private int[] ReadSchema() + { + Stream.Seek(_curPos + _curHdr.SchemaOffset, SeekOrigin.Begin); + + var count = _curHdr.SchemaFieldCount; + + var offsetSize = _curHdr.SchemaFieldOffsetSize; + + var res = new int[count]; + + for (int i = 0; i < count; i++) + { + res[i] = Stream.ReadInt(); + Stream.Seek(offsetSize, SeekOrigin.Current); + } + + return res; + } + /// <summary> + /// Reads the handle object. + /// </summary> + private T ReadHandleObject<T>(int pos) + { + // Get handle position. + int hndPos = pos - Stream.ReadInt(); + + int retPos = Stream.Position; + + try + { + object hndObj; + + if (_builder == null || !_builder.TryGetCachedField(hndPos, out hndObj)) + { + if (_hnds == null || !_hnds.TryGetValue(hndPos, out hndObj)) + { + // No such handler, i.e. we trying to deserialize inner object before deserializing outer. + Stream.Seek(hndPos, SeekOrigin.Begin); + + hndObj = Deserialize<T>(); + } + + // Notify builder that we deserialized object on other location. + if (_builder != null) + _builder.CacheField(hndPos, hndObj); + } + + return (T) hndObj; + } + finally + { + // Position stream to correct place. + Stream.Seek(retPos, SeekOrigin.Begin); + } + } + + /// <summary> + /// Adds a handle to the dictionary. + /// </summary> + /// <param name="pos">Position.</param> + /// <param name="obj">Object.</param> + private void AddHandle(int pos, object obj) + { + if (_hnds == null) + _hnds = new BinaryReaderHandleDictionary(pos, obj); + else + _hnds.Add(pos, obj); + } + + /// <summary> + /// Underlying stream. + /// </summary> + public IBinaryStream Stream + { + get; + private set; + } + + /// <summary> + /// Mark current output as raw. + /// </summary> + private void MarkRaw() + { + if (!_curRaw) + { + _curRaw = true; + + Stream.Seek(_curPos + _curHdr.GetRawOffset(Stream, _curPos), SeekOrigin.Begin); + } + } + + /// <summary> + /// Determines whether header at current position is HDR_NULL. + /// </summary> + private bool IsNotNullHeader(byte expHdr) + { + var hdr = ReadByte(); + + if (hdr == BinaryUtils.HdrNull) + return false; + + if (expHdr != hdr) + throw new BinaryObjectException(string.Format("Invalid header on deserialization. " + + "Expected: {0} but was: {1}", expHdr, hdr)); + + return true; + } + + /// <summary> + /// Seeks the field by name, reads header and returns true if field is present and header is not null. + /// </summary> + private bool SeekField(string fieldName, byte expHdr) + { + if (!SeekField(fieldName)) + return false; + + // Expected read order, no need to seek. + return IsNotNullHeader(expHdr); + } + + /// <summary> + /// Seeks the field by name. + /// </summary> + private bool SeekField(string fieldName) + { + if (_curRaw) + throw new BinaryObjectException("Cannot read named fields after raw data is read."); + + if (_curHdr.IsRawOnly) + return false; + + var actionId = _curStruct.CurStructAction; + + var fieldId = _curStruct.GetFieldId(fieldName); + + if (_curSchema == null || actionId >= _curSchema.Length || fieldId != _curSchema[actionId]) + { + _curSchema = null; // read order is different, ignore schema for future reads + + _curSchemaMap = _curSchemaMap ?? _curHdr.ReadSchemaAsDictionary(Stream, _curPos); + + int pos; + + if (!_curSchemaMap.TryGetValue(fieldId, out pos)) + return false; + + Stream.Seek(pos, SeekOrigin.Begin); + } + + return true; + } + + /// <summary> + /// Seeks specified field and invokes provided func. + /// </summary> + private T ReadField<T>(string fieldName, Func<IBinaryStream, T> readFunc, byte expHdr) + { + return SeekField(fieldName, expHdr) ? readFunc(Stream) : default(T); + } + + /// <summary> + /// Seeks specified field and invokes provided func. + /// </summary> + private T ReadField<T>(string fieldName, Func<BinaryReader, T> readFunc, byte expHdr) + { + return SeekField(fieldName, expHdr) ? readFunc(this) : default(T); + } + + /// <summary> + /// Seeks specified field and invokes provided func. + /// </summary> + private T ReadField<T>(string fieldName, Func<T> readFunc, byte expHdr) + { + return SeekField(fieldName, expHdr) ? readFunc() : default(T); + } + + /// <summary> + /// Reads header and invokes specified func if the header is not null. + /// </summary> + private T Read<T>(Func<BinaryReader, T> readFunc, byte expHdr) + { + return IsNotNullHeader(expHdr) ? readFunc(this) : default(T); + } + + /// <summary> + /// Reads header and invokes specified func if the header is not null. + /// </summary> + private T Read<T>(Func<IBinaryStream, T> readFunc, byte expHdr) + { + return IsNotNullHeader(expHdr) ? readFunc(Stream) : default(T); + } + } +}
http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReaderExtensions.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReaderExtensions.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReaderExtensions.cs new file mode 100644 index 0000000..c3dcc3a --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReaderExtensions.cs @@ -0,0 +1,52 @@ +/* + * 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.Collections.Generic; + using Apache.Ignite.Core.Binary; + + /// <summary> + /// Reader extensions. + /// </summary> + internal static class BinaryReaderExtensions + { + /// <summary> + /// Reads untyped collection as a generic list. + /// </summary> + /// <typeparam name="T">Type of list element.</typeparam> + /// <param name="reader">The reader.</param> + /// <returns>Resulting generic list.</returns> + public static List<T> ReadCollectionAsList<T>(this IBinaryRawReader reader) + { + return ((List<T>) reader.ReadCollection(size => new List<T>(size), + (col, elem) => ((List<T>) col).Add((T) elem))); + } + + /// <summary> + /// Reads untyped dictionary as generic dictionary. + /// </summary> + /// <typeparam name="TKey">The type of the key.</typeparam> + /// <typeparam name="TValue">The type of the value.</typeparam> + /// <param name="reader">The reader.</param> + /// <returns>Resulting dictionary.</returns> + public static Dictionary<TKey, TValue> ReadDictionaryAsGeneric<TKey, TValue>(this IBinaryRawReader reader) + { + return (Dictionary<TKey, TValue>) reader.ReadDictionary(size => new Dictionary<TKey, TValue>(size)); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReaderHandleDictionary.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReaderHandleDictionary.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReaderHandleDictionary.cs new file mode 100644 index 0000000..c145e7f --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReaderHandleDictionary.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 +{ + /// <summary> + /// Object handle dictionary for <see cref="BinaryReader"/>. + /// </summary> + internal class BinaryReaderHandleDictionary : BinaryHandleDictionary<int, object> + { + /// <summary> + /// Constructor with initial key-value pair. + /// </summary> + /// <param name="key">Key.</param> + /// <param name="val">Value.</param> + public BinaryReaderHandleDictionary(int key, object val) + : base(key, val) + { + // No-op. + } + + /** <inheritdoc /> */ + protected override int EmptyKey + { + get { return -1; } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs new file mode 100644 index 0000000..b229898 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveActions.cs @@ -0,0 +1,440 @@ +/* + * 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; + using System.Diagnostics; + using System.Linq.Expressions; + using System.Reflection; + using Apache.Ignite.Core.Binary; + using Apache.Ignite.Core.Common; + using Apache.Ignite.Core.Impl.Common; + + /// <summary> + /// Write action delegate. + /// </summary> + /// <param name="obj">Target object.</param> + /// <param name="writer">Writer.</param> + internal delegate void BinaryReflectiveWriteAction(object obj, IBinaryWriter writer); + + /// <summary> + /// Read action delegate. + /// </summary> + /// <param name="obj">Target object.</param> + /// <param name="reader">Reader.</param> + internal delegate void BinaryReflectiveReadAction(object obj, IBinaryReader reader); + + /// <summary> + /// Routines for reflective reads and writes. + /// </summary> + internal static class BinaryReflectiveActions + { + /** Method: read enum. */ + private static readonly MethodInfo MthdReadEnum = + typeof(IBinaryReader).GetMethod("ReadEnum", new[] { typeof(string) }); + + /** Method: read enum array. */ + private static readonly MethodInfo MthdReadEnumArray = + typeof(IBinaryReader).GetMethod("ReadEnumArray", new[] { typeof(string) }); + + /** Method: read array. */ + private static readonly MethodInfo MthdReadObjArray = + typeof(IBinaryReader).GetMethod("ReadArray", new[] { typeof(string) }); + + /** Method: read object. */ + private static readonly MethodInfo MthdReadObj= + typeof(IBinaryReader).GetMethod("ReadObject", new[] { typeof(string) }); + + /** Method: write enum array. */ + private static readonly MethodInfo MthdWriteEnumArray = + typeof(IBinaryWriter).GetMethod("WriteEnumArray"); + + /** Method: write array. */ + private static readonly MethodInfo MthdWriteObjArray = + typeof(IBinaryWriter).GetMethod("WriteArray"); + + /** Method: read object. */ + private static readonly MethodInfo MthdWriteObj = + typeof(IBinaryWriter).GetMethod("WriteObject"); + + /// <summary> + /// Lookup read/write actions for the given type. + /// </summary> + /// <param name="field">The field.</param> + /// <param name="writeAction">Write action.</param> + /// <param name="readAction">Read action.</param> + public static void TypeActions(FieldInfo field, out BinaryReflectiveWriteAction writeAction, + out BinaryReflectiveReadAction readAction) + { + var type = field.FieldType; + + if (type.IsPrimitive) + HandlePrimitive(field, out writeAction, out readAction); + else if (type.IsArray) + HandleArray(field, out writeAction, out readAction); + else + HandleOther(field, out writeAction, out readAction); + } + + /// <summary> + /// Handle primitive type. + /// </summary> + /// <param name="field">The field.</param> + /// <param name="writeAction">Write action.</param> + /// <param name="readAction">Read action.</param> + /// <exception cref="IgniteException">Unsupported primitive type: + type.Name</exception> + private static void HandlePrimitive(FieldInfo field, out BinaryReflectiveWriteAction writeAction, + out BinaryReflectiveReadAction readAction) + { + var type = field.FieldType; + + if (type == typeof(bool)) + { + writeAction = GetWriter<bool>(field, (f, w, o) => w.WriteBoolean(f, o)); + readAction = GetReader(field, (f, r) => r.ReadBoolean(f)); + } + else if (type == typeof(sbyte)) + { + writeAction = GetWriter<sbyte>(field, (f, w, o) => w.WriteByte(f, unchecked((byte) o))); + readAction = GetReader(field, (f, r) => unchecked ((sbyte)r.ReadByte(f))); + } + else if (type == typeof(byte)) + { + writeAction = GetWriter<byte>(field, (f, w, o) => w.WriteByte(f, o)); + readAction = GetReader(field, (f, r) => r.ReadByte(f)); + } + else if (type == typeof(short)) + { + writeAction = GetWriter<short>(field, (f, w, o) => w.WriteShort(f, o)); + readAction = GetReader(field, (f, r) => r.ReadShort(f)); + } + else if (type == typeof(ushort)) + { + writeAction = GetWriter<ushort>(field, (f, w, o) => w.WriteShort(f, unchecked((short) o))); + readAction = GetReader(field, (f, r) => unchecked((ushort) r.ReadShort(f))); + } + else if (type == typeof(char)) + { + writeAction = GetWriter<char>(field, (f, w, o) => w.WriteChar(f, o)); + readAction = GetReader(field, (f, r) => r.ReadChar(f)); + } + else if (type == typeof(int)) + { + writeAction = GetWriter<int>(field, (f, w, o) => w.WriteInt(f, o)); + readAction = GetReader(field, (f, r) => r.ReadInt(f)); + } + else if (type == typeof(uint)) + { + writeAction = GetWriter<uint>(field, (f, w, o) => w.WriteInt(f, unchecked((int) o))); + readAction = GetReader(field, (f, r) => unchecked((uint) r.ReadInt(f))); + } + else if (type == typeof(long)) + { + writeAction = GetWriter<long>(field, (f, w, o) => w.WriteLong(f, o)); + readAction = GetReader(field, (f, r) => r.ReadLong(f)); + } + else if (type == typeof(ulong)) + { + writeAction = GetWriter<ulong>(field, (f, w, o) => w.WriteLong(f, unchecked((long) o))); + readAction = GetReader(field, (f, r) => unchecked((ulong) r.ReadLong(f))); + } + else if (type == typeof(float)) + { + writeAction = GetWriter<float>(field, (f, w, o) => w.WriteFloat(f, o)); + readAction = GetReader(field, (f, r) => r.ReadFloat(f)); + } + else if (type == typeof(double)) + { + writeAction = GetWriter<double>(field, (f, w, o) => w.WriteDouble(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDouble(f)); + } + else + throw new IgniteException("Unsupported primitive type: " + type.Name); + } + + /// <summary> + /// Handle array type. + /// </summary> + /// <param name="field">The field.</param> + /// <param name="writeAction">Write action.</param> + /// <param name="readAction">Read action.</param> + private static void HandleArray(FieldInfo field, out BinaryReflectiveWriteAction writeAction, + out BinaryReflectiveReadAction readAction) + { + Type elemType = field.FieldType.GetElementType(); + + if (elemType == typeof(bool)) + { + writeAction = GetWriter<bool[]>(field, (f, w, o) => w.WriteBooleanArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadBooleanArray(f)); + } + else if (elemType == typeof(byte)) + { + writeAction = GetWriter<byte[]>(field, (f, w, o) => w.WriteByteArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadByteArray(f)); + } + else if (elemType == typeof(sbyte)) + { + writeAction = GetWriter<sbyte[]>(field, (f, w, o) => w.WriteByteArray(f, (byte[]) (Array) o)); + readAction = GetReader(field, (f, r) => (sbyte[]) (Array) r.ReadByteArray(f)); + } + else if (elemType == typeof(short)) + { + writeAction = GetWriter<short[]>(field, (f, w, o) => w.WriteShortArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadShortArray(f)); + } + else if (elemType == typeof(ushort)) + { + writeAction = GetWriter<ushort[]>(field, (f, w, o) => w.WriteShortArray(f, (short[]) (Array) o)); + readAction = GetReader(field, (f, r) => (ushort[]) (Array) r.ReadShortArray(f)); + } + else if (elemType == typeof(char)) + { + writeAction = GetWriter<char[]>(field, (f, w, o) => w.WriteCharArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadCharArray(f)); + } + else if (elemType == typeof(int)) + { + writeAction = GetWriter<int[]>(field, (f, w, o) => w.WriteIntArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadIntArray(f)); + } + else if (elemType == typeof(uint)) + { + writeAction = GetWriter<uint[]>(field, (f, w, o) => w.WriteIntArray(f, (int[]) (Array) o)); + readAction = GetReader(field, (f, r) => (uint[]) (Array) r.ReadIntArray(f)); + } + else if (elemType == typeof(long)) + { + writeAction = GetWriter<long[]>(field, (f, w, o) => w.WriteLongArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadLongArray(f)); + } + else if (elemType == typeof(ulong)) + { + writeAction = GetWriter<ulong[]>(field, (f, w, o) => w.WriteLongArray(f, (long[]) (Array) o)); + readAction = GetReader(field, (f, r) => (ulong[]) (Array) r.ReadLongArray(f)); + } + else if (elemType == typeof(float)) + { + writeAction = GetWriter<float[]>(field, (f, w, o) => w.WriteFloatArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadFloatArray(f)); + } + else if (elemType == typeof(double)) + { + writeAction = GetWriter<double[]>(field, (f, w, o) => w.WriteDoubleArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDoubleArray(f)); + } + else if (elemType == typeof(decimal?)) + { + writeAction = GetWriter<decimal?[]>(field, (f, w, o) => w.WriteDecimalArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDecimalArray(f)); + } + else if (elemType == typeof(string)) + { + writeAction = GetWriter<string[]>(field, (f, w, o) => w.WriteStringArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadStringArray(f)); + } + else if (elemType == typeof(Guid?)) + { + writeAction = GetWriter<Guid?[]>(field, (f, w, o) => w.WriteGuidArray(f, o)); + readAction = GetReader(field, (f, r) => r.ReadGuidArray(f)); + } + else if (elemType.IsEnum) + { + writeAction = GetWriter(field, MthdWriteEnumArray, elemType); + readAction = GetReader(field, MthdReadEnumArray, elemType); + } + else + { + writeAction = GetWriter(field, MthdWriteObjArray, elemType); + readAction = GetReader(field, MthdReadObjArray, elemType); + } + } + + /// <summary> + /// Handle other type. + /// </summary> + /// <param name="field">The field.</param> + /// <param name="writeAction">Write action.</param> + /// <param name="readAction">Read action.</param> + private static void HandleOther(FieldInfo field, out BinaryReflectiveWriteAction writeAction, + out BinaryReflectiveReadAction readAction) + { + var type = field.FieldType; + + var genericDef = type.IsGenericType ? type.GetGenericTypeDefinition() : null; + + bool nullable = genericDef == typeof(Nullable<>); + + var nullableType = nullable ? type.GetGenericArguments()[0] : null; + + if (type == typeof(decimal)) + { + writeAction = GetWriter<decimal>(field, (f, w, o) => w.WriteDecimal(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDecimal(f)); + } + else if (type == typeof(string)) + { + writeAction = GetWriter<string>(field, (f, w, o) => w.WriteString(f, o)); + readAction = GetReader(field, (f, r) => r.ReadString(f)); + } + else if (type == typeof(Guid)) + { + writeAction = GetWriter<Guid>(field, (f, w, o) => w.WriteGuid(f, o)); + readAction = GetReader(field, (f, r) => r.ReadObject<Guid>(f)); + } + else if (nullable && nullableType == typeof(Guid)) + { + writeAction = GetWriter<Guid?>(field, (f, w, o) => w.WriteGuid(f, o)); + readAction = GetReader(field, (f, r) => r.ReadGuid(f)); + } + else if (type.IsEnum) + { + writeAction = GetWriter<object>(field, (f, w, o) => w.WriteEnum(f, o), true); + readAction = GetReader(field, MthdReadEnum); + } + else if (type == BinaryUtils.TypDictionary || type.GetInterface(BinaryUtils.TypDictionary.FullName) != null && !type.IsGenericType) + { + writeAction = GetWriter<IDictionary>(field, (f, w, o) => w.WriteDictionary(f, o)); + readAction = GetReader(field, (f, r) => r.ReadDictionary(f)); + } + else if (type == BinaryUtils.TypCollection || type.GetInterface(BinaryUtils.TypCollection.FullName) != null && !type.IsGenericType) + { + writeAction = GetWriter<ICollection>(field, (f, w, o) => w.WriteCollection(f, o)); + readAction = GetReader(field, (f, r) => r.ReadCollection(f)); + } + else + { + writeAction = GetWriter(field, MthdWriteObj); + readAction = GetReader(field, MthdReadObj); + } + } + + /// <summary> + /// Gets the reader with a specified write action. + /// </summary> + private static BinaryReflectiveWriteAction GetWriter<T>(FieldInfo field, + Expression<Action<string, IBinaryWriter, T>> write, + bool convertFieldValToObject = false) + { + Debug.Assert(field != null); + Debug.Assert(field.DeclaringType != null); // non-static + + // Get field value + var targetParam = Expression.Parameter(typeof(object)); + var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType); + Expression fldExpr = Expression.Field(targetParamConverted, field); + + if (convertFieldValToObject) + fldExpr = Expression.Convert(fldExpr, typeof (object)); + + // Call Writer method + var writerParam = Expression.Parameter(typeof(IBinaryWriter)); + var fldNameParam = Expression.Constant(BinaryUtils.CleanFieldName(field.Name)); + var writeExpr = Expression.Invoke(write, fldNameParam, writerParam, fldExpr); + + // Compile and return + return Expression.Lambda<BinaryReflectiveWriteAction>(writeExpr, targetParam, writerParam).Compile(); + } + + /// <summary> + /// Gets the writer with a specified generic method. + /// </summary> + private static BinaryReflectiveWriteAction GetWriter(FieldInfo field, MethodInfo method, + params Type[] genericArgs) + { + Debug.Assert(field != null); + Debug.Assert(field.DeclaringType != null); // non-static + + if (genericArgs.Length == 0) + genericArgs = new[] {field.FieldType}; + + // Get field value + var targetParam = Expression.Parameter(typeof(object)); + var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType); + var fldExpr = Expression.Field(targetParamConverted, field); + + // Call Writer method + var writerParam = Expression.Parameter(typeof(IBinaryWriter)); + var fldNameParam = Expression.Constant(BinaryUtils.CleanFieldName(field.Name)); + var writeMethod = method.MakeGenericMethod(genericArgs); + var writeExpr = Expression.Call(writerParam, writeMethod, fldNameParam, fldExpr); + + // Compile and return + return Expression.Lambda<BinaryReflectiveWriteAction>(writeExpr, targetParam, writerParam).Compile(); + } + + /// <summary> + /// Gets the reader with a specified read action. + /// </summary> + private static BinaryReflectiveReadAction GetReader<T>(FieldInfo field, + Expression<Func<string, IBinaryReader, T>> read) + { + Debug.Assert(field != null); + Debug.Assert(field.DeclaringType != null); // non-static + + // Call Reader method + var readerParam = Expression.Parameter(typeof(IBinaryReader)); + var fldNameParam = Expression.Constant(BinaryUtils.CleanFieldName(field.Name)); + Expression readExpr = Expression.Invoke(read, fldNameParam, readerParam); + + if (typeof(T) != field.FieldType) + readExpr = Expression.Convert(readExpr, field.FieldType); + + // Assign field value + var targetParam = Expression.Parameter(typeof(object)); + var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType); + var assignExpr = Expression.Call(DelegateConverter.GetWriteFieldMethod(field), targetParamConverted, + readExpr); + + // Compile and return + return Expression.Lambda<BinaryReflectiveReadAction>(assignExpr, targetParam, readerParam).Compile(); + } + + /// <summary> + /// Gets the reader with a specified generic method. + /// </summary> + private static BinaryReflectiveReadAction GetReader(FieldInfo field, MethodInfo method, + params Type[] genericArgs) + { + Debug.Assert(field != null); + Debug.Assert(field.DeclaringType != null); // non-static + + if (genericArgs.Length == 0) + genericArgs = new[] {field.FieldType}; + + // Call Reader method + var readerParam = Expression.Parameter(typeof (IBinaryReader)); + var fldNameParam = Expression.Constant(BinaryUtils.CleanFieldName(field.Name)); + var readMethod = method.MakeGenericMethod(genericArgs); + Expression readExpr = Expression.Call(readerParam, readMethod, fldNameParam); + + if (readMethod.ReturnType != field.FieldType) + readExpr = Expression.Convert(readExpr, field.FieldType); + + // Assign field value + var targetParam = Expression.Parameter(typeof(object)); + var targetParamConverted = Expression.Convert(targetParam, field.DeclaringType); + var assignExpr = Expression.Call(DelegateConverter.GetWriteFieldMethod(field), targetParamConverted, + readExpr); + + // Compile and return + return Expression.Lambda<BinaryReflectiveReadAction>(assignExpr, targetParam, readerParam).Compile(); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializer.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializer.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializer.cs new file mode 100644 index 0000000..0804c25 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReflectiveSerializer.cs @@ -0,0 +1,218 @@ +/* + * 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.Reflection; + using Apache.Ignite.Core.Binary; + + /// <summary> + /// Binary serializer which reflectively writes all fields except of ones with + /// <see cref="System.NonSerializedAttribute"/>. + /// <para /> + /// Note that Java platform stores dates as a difference between current time + /// and predefined absolute UTC date. Therefore, this difference is always the + /// same for all time zones. .Net, in contrast, stores dates as a difference + /// between current time and some predefined date relative to the current time + /// zone. It means that this difference will be different as you change time zones. + /// To overcome this discrepancy Ignite always converts .Net date to UTC form + /// before serializing and allows user to decide whether to deserialize them + /// in UTC or local form using <c>ReadTimestamp(..., true/false)</c> methods in + /// <see cref="IBinaryReader"/> and <see cref="IBinaryRawReader"/>. + /// This serializer always read dates in UTC form. It means that if you have + /// local date in any field/property, it will be implicitly converted to UTC + /// form after the first serialization-deserialization cycle. + /// </summary> + internal class BinaryReflectiveSerializer : IBinarySerializer + { + /** Cached binding flags. */ + private static readonly BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | + BindingFlags.NonPublic | BindingFlags.DeclaredOnly; + + /** Cached type descriptors. */ + private readonly IDictionary<Type, Descriptor> _types = new Dictionary<Type, Descriptor>(); + + /// <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. + /// </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> + public void 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); + + _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; + + /** Read actions to be performed. */ + private readonly List<BinaryReflectiveReadAction> _rActions; + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="fields">Fields.</param> + public Descriptor(List<FieldInfo> fields) + { + _wActions = new List<BinaryReflectiveWriteAction>(fields.Count); + _rActions = new List<BinaryReflectiveReadAction>(fields.Count); + + foreach (FieldInfo field in fields) + { + BinaryReflectiveWriteAction writeAction; + BinaryReflectiveReadAction readAction; + + BinaryReflectiveActions.TypeActions(field, out writeAction, out readAction); + + _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); + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/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 new file mode 100644 index 0000000..247b40d --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySurrogateTypeDescriptor.cs @@ -0,0 +1,162 @@ +/* + * 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 Apache.Ignite.Core.Binary; + using Apache.Ignite.Core.Impl.Binary.Structure; + + /// <summary> + /// Surrogate type descriptor. Used in cases when type if identified by name and + /// is not provided in configuration. + /// </summary> + internal class BinarySurrogateTypeDescriptor : IBinaryTypeDescriptor + { + /** Binary configuration. */ + private readonly BinaryConfiguration _cfg; + + /** Type ID. */ + private readonly int _id; + + /** Type name. */ + private readonly string _name; + + /** Type structure. */ + private volatile BinaryStructure _writerTypeStruct = BinaryStructure.CreateEmpty(); + + /** Type structure. */ + private BinaryStructure _readerTypeStructure = BinaryStructure.CreateEmpty(); + + /** Type schema. */ + private readonly BinaryObjectSchema _schema = new BinaryObjectSchema(); + + /// <summary> + /// Constructor. + /// </summary> + /// <param name="cfg">Configuration.</param> + /// <param name="id">Type ID.</param> + public BinarySurrogateTypeDescriptor(BinaryConfiguration cfg, int id) + { + _cfg = cfg; + _id = id; + } + + /// <summary> + /// Constrcutor. + /// </summary> + /// <param name="cfg">Configuration.</param> + /// <param name="name">Type name.</param> + public BinarySurrogateTypeDescriptor(BinaryConfiguration cfg, string name) + { + _cfg = cfg; + _name = name; + + _id = BinaryUtils.TypeId(name, cfg.DefaultNameMapper, cfg.DefaultIdMapper); + } + + /** <inheritDoc /> */ + public Type Type + { + get { return null; } + } + + /** <inheritDoc /> */ + public int TypeId + { + get { return _id; } + } + + /** <inheritDoc /> */ + public string TypeName + { + get { return _name; } + } + + /** <inheritDoc /> */ + public bool UserType + { + get { return true; } + } + + /** <inheritDoc /> */ + public bool KeepDeserialized + { + get { return _cfg.DefaultKeepDeserialized; } + } + + /** <inheritDoc /> */ + public IBinaryNameMapper NameMapper + { + get { return _cfg.DefaultNameMapper; } + } + + /** <inheritDoc /> */ + public IBinaryIdMapper IdMapper + { + get { return _cfg.DefaultIdMapper; } + } + + /** <inheritDoc /> */ + public IBinarySerializer Serializer + { + get { return _cfg.DefaultSerializer; } + } + + /** <inheritDoc /> */ + public string AffinityKeyFieldName + { + get { return null; } + } + + /** <inheritDoc /> */ + public BinaryStructure WriterTypeStructure + { + get { return _writerTypeStruct; } + } + + public BinaryStructure ReaderTypeStructure + { + get { return _readerTypeStructure; } + } + + /** <inheritDoc /> */ + public void UpdateWriteStructure(BinaryStructure exp, int pathIdx, IList<BinaryStructureUpdate> updates) + { + lock (this) + { + _writerTypeStruct = _writerTypeStruct.Merge(exp, pathIdx, updates); + } + } + + /** <inheritDoc /> */ + public void UpdateReadStructure(BinaryStructure exp, int pathIdx, IList<BinaryStructureUpdate> updates) + { + lock (this) + { + _readerTypeStructure = _readerTypeStructure.Merge(exp, pathIdx, updates); + } + } + + /** <inheritDoc /> */ + public BinaryObjectSchema Schema + { + get { return _schema; } + } + } +}
