ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From voze...@apache.org
Subject [18/26] ignite git commit: IGNITE-1845: Adopted new binary API in .Net.
Date Wed, 11 Nov 2015 09:15:51 GMT
http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
new file mode 100644
index 0000000..1840ab9
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectBuilder.cs
@@ -0,0 +1,1128 @@
+/*
+ * 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;
+    using System.IO;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Common;
+    using Apache.Ignite.Core.Impl.Binary.IO;
+    using Apache.Ignite.Core.Impl.Binary.Metadata;
+
+    /// <summary>
+    /// Binary builder implementation.
+    /// </summary>
+    internal class BinaryObjectBuilder : IBinaryObjectBuilder
+    {
+        /** Cached dictionary with no values. */
+        private static readonly IDictionary<int, BinaryBuilderField> EmptyVals =
+            new Dictionary<int, BinaryBuilderField>();
+        
+        /** Binary. */
+        private readonly IgniteBinary _igniteBinary;
+
+        /** */
+        private readonly BinaryObjectBuilder _parent;
+
+        /** Initial binary object. */
+        private readonly BinaryObject _obj;
+
+        /** Type descriptor. */
+        private readonly IBinaryTypeDescriptor _desc;
+
+        /** Values. */
+        private IDictionary<string, BinaryBuilderField> _vals;
+
+        /** Contextual fields. */
+        private IDictionary<int, BinaryBuilderField> _cache;
+
+        /** Hash code. */
+        private int _hashCode;
+        
+        /** Current context. */
+        private Context _ctx;
+
+        /** Write array action. */
+        private static readonly Action<BinaryWriter, object> WriteArrayAction = 
+            (w, o) => w.WriteArrayInternal((Array) o);
+
+        /** Write collection action. */
+        private static readonly Action<BinaryWriter, object> WriteCollectionAction = 
+            (w, o) => w.WriteCollection((ICollection) o);
+
+        /** Write timestamp action. */
+        private static readonly Action<BinaryWriter, object> WriteTimestampAction = 
+            (w, o) => w.WriteTimestamp((DateTime?) o);
+
+        /** Write timestamp array action. */
+        private static readonly Action<BinaryWriter, object> WriteTimestampArrayAction = 
+            (w, o) => w.WriteTimestampArray((DateTime?[])o);
+
+        /// <summary>
+        /// Constructor.
+        /// </summary>
+        /// <param name="igniteBinary">Binary.</param>
+        /// <param name="parent">Parent builder.</param>
+        /// <param name="obj">Initial binary object.</param>
+        /// <param name="desc">Type descriptor.</param>
+        public BinaryObjectBuilder(IgniteBinary igniteBinary, BinaryObjectBuilder parent, 
+            BinaryObject obj, IBinaryTypeDescriptor desc)
+        {
+            Debug.Assert(igniteBinary != null);
+            Debug.Assert(obj != null);
+            Debug.Assert(desc != null);
+
+            _igniteBinary = igniteBinary;
+            _parent = parent ?? this;
+            _obj = obj;
+            _desc = desc;
+
+            _hashCode = obj.GetHashCode();
+        }
+
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetHashCode(int hashCode)
+        {
+            _hashCode = hashCode;
+
+            return this;
+        }
+
+        /** <inheritDoc /> */
+        public T GetField<T>(string name)
+        {
+            BinaryBuilderField field;
+
+            if (_vals != null && _vals.TryGetValue(name, out field))
+                return field != BinaryBuilderField.RmvMarker ? (T) field.Value : default(T);
+
+            int pos;
+
+            if (!_obj.TryGetFieldPosition(name, out pos))
+                return default(T);
+
+            T val;
+
+            if (TryGetCachedField(pos, out val))
+                return val;
+
+            val = _obj.GetField<T>(pos, this);
+
+            var fld = CacheField(pos, val);
+
+            SetField0(name, fld);
+
+            return val;
+        }
+
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetField<T>(string fieldName, T val)
+        {
+            return SetField0(fieldName,
+                new BinaryBuilderField(typeof (T), val, BinarySystemHandlers.GetTypeId(typeof (T))));
+        }
+
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetArrayField<T>(string fieldName, T[] val)
+        {
+            return SetField0(fieldName,
+                new BinaryBuilderField(typeof (T[]), val, BinaryUtils.TypeArray, WriteArrayAction));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetBooleanField(string fieldName, bool val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (bool), val, BinaryUtils.TypeBool, 
+                (w, o) => w.WriteBoolean((bool) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetBooleanArrayField(string fieldName, bool[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (bool[]), val, BinaryUtils.TypeArrayBool,
+                (w, o) => w.WriteBooleanArray((bool[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetByteField(string fieldName, byte val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (byte), val, BinaryUtils.TypeByte,
+                (w, o) => w.WriteByte((byte) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetByteArrayField(string fieldName, byte[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (byte[]), val, BinaryUtils.TypeArrayByte,
+                (w, o) => w.WriteByteArray((byte[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetCharField(string fieldName, char val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (char), val, BinaryUtils.TypeChar,
+                (w, o) => w.WriteChar((char) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetCharArrayField(string fieldName, char[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (char[]), val, BinaryUtils.TypeArrayChar,
+                (w, o) => w.WriteCharArray((char[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetCollectionField(string fieldName, ICollection val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (ICollection), val, BinaryUtils.TypeCollection,
+                WriteCollectionAction));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetDecimalField(string fieldName, decimal? val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (decimal?), val, BinaryUtils.TypeDecimal,
+                (w, o) => w.WriteDecimal((decimal?) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetDecimalArrayField(string fieldName, decimal?[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (decimal?[]), val, BinaryUtils.TypeArrayDecimal,
+                (w, o) => w.WriteDecimalArray((decimal?[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetDictionaryField(string fieldName, IDictionary val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (IDictionary), val, BinaryUtils.TypeDictionary,
+                (w, o) => w.WriteDictionary((IDictionary) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetDoubleField(string fieldName, double val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (double), val, BinaryUtils.TypeDouble,
+                (w, o) => w.WriteDouble((double) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetDoubleArrayField(string fieldName, double[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (double[]), val, BinaryUtils.TypeArrayDouble,
+                (w, o) => w.WriteDoubleArray((double[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetEnumField<T>(string fieldName, T val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (T), val, BinaryUtils.TypeEnum,
+                (w, o) => w.WriteEnum((T) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetEnumArrayField<T>(string fieldName, T[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (T[]), val, BinaryUtils.TypeArrayEnum,
+                (w, o) => w.WriteEnumArray((T[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetFloatField(string fieldName, float val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (float), val, BinaryUtils.TypeFloat,
+                (w, o) => w.WriteFloat((float) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetFloatArrayField(string fieldName, float[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (float[]), val, BinaryUtils.TypeArrayFloat,
+                (w, o) => w.WriteFloatArray((float[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetGuidField(string fieldName, Guid? val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (Guid?), val, BinaryUtils.TypeGuid,
+                (w, o) => w.WriteGuid((Guid?) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetGuidArrayField(string fieldName, Guid?[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (Guid?[]), val, BinaryUtils.TypeArrayGuid,
+                (w, o) => w.WriteGuidArray((Guid?[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetIntField(string fieldName, int val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (int), val, BinaryUtils.TypeInt,
+                (w, o) => w.WriteInt((int) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetIntArrayField(string fieldName, int[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (int[]), val, BinaryUtils.TypeArrayInt,
+                (w, o) => w.WriteIntArray((int[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetLongField(string fieldName, long val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (long), val, BinaryUtils.TypeLong,
+                (w, o) => w.WriteLong((long) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetLongArrayField(string fieldName, long[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (long[]), val, BinaryUtils.TypeArrayLong,
+                (w, o) => w.WriteLongArray((long[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetShortField(string fieldName, short val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (short), val, BinaryUtils.TypeShort,
+                (w, o) => w.WriteShort((short) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetShortArrayField(string fieldName, short[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (short[]), val, BinaryUtils.TypeArrayShort,
+                (w, o) => w.WriteShortArray((short[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetStringField(string fieldName, string val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (string), val, BinaryUtils.TypeString,
+                (w, o) => w.WriteString((string) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetStringArrayField(string fieldName, string[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (string[]), val, BinaryUtils.TypeArrayString,
+                (w, o) => w.WriteStringArray((string[]) o)));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetTimestampField(string fieldName, DateTime? val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (DateTime?), val, BinaryUtils.TypeTimestamp,
+                WriteTimestampAction));
+        }
+ 
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder SetTimestampArrayField(string fieldName, DateTime?[] val)
+        {
+            return SetField0(fieldName, new BinaryBuilderField(typeof (DateTime?[]), val, BinaryUtils.TypeArrayTimestamp,
+                WriteTimestampArrayAction));
+        } 
+
+        /** <inheritDoc /> */
+        public IBinaryObjectBuilder RemoveField(string name)
+        {
+            return SetField0(name, BinaryBuilderField.RmvMarker);
+        }
+
+        /** <inheritDoc /> */
+        public IBinaryObject Build()
+        {
+            BinaryHeapStream inStream = new BinaryHeapStream(_obj.Data);
+
+            inStream.Seek(_obj.Offset, SeekOrigin.Begin);
+
+            // Assume that resulting length will be no less than header + [fields_cnt] * 12;
+            int estimatedCapacity = BinaryObjectHeader.Size + (_vals == null ? 0 : _vals.Count*12);
+
+            BinaryHeapStream outStream = new BinaryHeapStream(estimatedCapacity);
+
+            BinaryWriter writer = _igniteBinary.Marshaller.StartMarshal(outStream);
+
+            writer.SetBuilder(this);
+
+            // All related builders will work in this context with this writer.
+            _parent._ctx = new Context(writer);
+            
+            try
+            {
+                // Write.
+                writer.Write(this);
+                
+                // Process metadata.
+                _igniteBinary.Marshaller.FinishMarshal(writer);
+
+                // Create binary object once metadata is processed.
+                return new BinaryObject(_igniteBinary.Marshaller, outStream.InternalArray, 0, 
+                    BinaryObjectHeader.Read(outStream, 0));
+            }
+            finally
+            {
+                // Cleanup.
+                _parent._ctx.Closed = true;
+            }
+        }
+
+        /// <summary>
+        /// Create child builder.
+        /// </summary>
+        /// <param name="obj">binary object.</param>
+        /// <returns>Child builder.</returns>
+        public BinaryObjectBuilder Child(BinaryObject obj)
+        {
+            var desc = _igniteBinary.Marshaller.GetDescriptor(true, obj.TypeId);
+
+            return new BinaryObjectBuilder(_igniteBinary, null, obj, desc);
+        }
+        
+        /// <summary>
+        /// Get cache field.
+        /// </summary>
+        /// <param name="pos">Position.</param>
+        /// <param name="val">Value.</param>
+        /// <returns><c>true</c> if value is found in cache.</returns>
+        public bool TryGetCachedField<T>(int pos, out T val)
+        {
+            if (_parent._cache != null)
+            {
+                BinaryBuilderField res;
+
+                if (_parent._cache.TryGetValue(pos, out res))
+                {
+                    val = res != null ? (T) res.Value : default(T);
+
+                    return true;
+                }
+            }
+
+            val = default(T);
+
+            return false;
+        }
+
+        /// <summary>
+        /// Add field to cache test.
+        /// </summary>
+        /// <param name="pos">Position.</param>
+        /// <param name="val">Value.</param>
+        public BinaryBuilderField CacheField<T>(int pos, T val)
+        {
+            if (_parent._cache == null)
+                _parent._cache = new Dictionary<int, BinaryBuilderField>(2);
+
+            var hdr = _obj.Data[pos];
+
+            var field = new BinaryBuilderField(typeof(T), val, hdr, GetWriteAction(hdr));
+            
+            _parent._cache[pos] = field;
+
+            return field;
+        }
+
+        /// <summary>
+        /// Gets the write action by header.
+        /// </summary>
+        /// <param name="header">The header.</param>
+        /// <returns>Write action.</returns>
+        private static Action<BinaryWriter, object> GetWriteAction(byte header)
+        {
+            // We need special actions for all cases where SetField(X) produces different result from SetSpecialField(X)
+            // Arrays, Collections, Dates
+
+            switch (header)
+            {
+                case BinaryUtils.TypeArray:
+                    return WriteArrayAction;
+
+                case BinaryUtils.TypeCollection:
+                    return WriteCollectionAction;
+
+                case BinaryUtils.TypeTimestamp:
+                    return WriteTimestampAction;
+
+                case BinaryUtils.TypeArrayTimestamp:
+                    return WriteTimestampArrayAction;
+            }
+
+            return null;
+        }
+
+        /// <summary>
+        /// Internal set field routine.
+        /// </summary>
+        /// <param name="fieldName">Name.</param>
+        /// <param name="val">Value.</param>
+        /// <returns>This builder.</returns>
+        private IBinaryObjectBuilder SetField0(string fieldName, BinaryBuilderField val)
+        {
+            if (_vals == null)
+                _vals = new Dictionary<string, BinaryBuilderField>();
+
+            _vals[fieldName] = val;
+
+            return this;
+        }
+
+        /// <summary>
+        /// Mutate binary object.
+        /// </summary>
+        /// <param name="inStream">Input stream with initial object.</param>
+        /// <param name="outStream">Output stream.</param>
+        /// <param name="desc">Type descriptor.</param>
+        /// <param name="hashCode">Hash code.</param>
+        /// <param name="vals">Values.</param>
+        private void Mutate(
+            BinaryHeapStream inStream,
+            BinaryHeapStream outStream,
+            IBinaryTypeDescriptor desc,
+            int hashCode, 
+            IDictionary<string, BinaryBuilderField> vals)
+        {
+            // Set correct builder to writer frame.
+            BinaryObjectBuilder oldBuilder = _parent._ctx.Writer.SetBuilder(_parent);
+
+            int streamPos = inStream.Position;
+            
+            try
+            {
+                // Prepare fields.
+                IBinaryTypeHandler metaHnd = _igniteBinary.Marshaller.GetBinaryTypeHandler(desc);
+
+                IDictionary<int, BinaryBuilderField> vals0;
+
+                if (vals == null || vals.Count == 0)
+                    vals0 = EmptyVals;
+                else
+                {
+                    vals0 = new Dictionary<int, BinaryBuilderField>(vals.Count);
+
+                    foreach (KeyValuePair<string, BinaryBuilderField> valEntry in vals)
+                    {
+                        int fieldId = BinaryUtils.FieldId(desc.TypeId, valEntry.Key, desc.NameMapper, desc.IdMapper);
+
+                        if (vals0.ContainsKey(fieldId))
+                            throw new IgniteException("Collision in field ID detected (change field name or " +
+                                "define custom ID mapper) [fieldName=" + valEntry.Key + ", fieldId=" + fieldId + ']');
+
+                        vals0[fieldId] = valEntry.Value;
+
+                        // Write metadata if: 1) it is enabled for type; 2) type is not null (i.e. it is neither 
+                        // remove marker, nor a field read through "GetField" method.
+                        if (metaHnd != null && valEntry.Value.Type != null)
+                            metaHnd.OnFieldWrite(fieldId, valEntry.Key, valEntry.Value.TypeId);
+                    }
+                }
+
+                // Actual processing.
+                Mutate0(_parent._ctx, inStream, outStream, true, hashCode, vals0);
+
+                // 3. Handle metadata.
+                if (metaHnd != null)
+                {
+                    IDictionary<string, int> meta = metaHnd.OnObjectWriteFinished();
+
+                    if (meta != null)
+                        _parent._ctx.Writer.SaveMetadata(desc.TypeId, desc.TypeName, desc.AffinityKeyFieldName, meta);
+                }
+            }
+            finally
+            {
+                // Restore builder frame.
+                _parent._ctx.Writer.SetBuilder(oldBuilder);
+
+                inStream.Seek(streamPos, SeekOrigin.Begin);
+            }
+        }
+
+        /// <summary>
+        /// Internal mutation routine.
+        /// </summary>
+        /// <param name="inStream">Input stream.</param>
+        /// <param name="outStream">Output stream.</param>
+        /// <param name="ctx">Context.</param>
+        /// <param name="changeHash">WHether hash should be changed.</param>
+        /// <param name="hash">New hash.</param>
+        /// <param name="vals">Values to be replaced.</param>
+        /// <returns>Mutated object.</returns>
+        private void Mutate0(Context ctx, BinaryHeapStream inStream, IBinaryStream outStream,
+            bool changeHash, int hash, IDictionary<int, BinaryBuilderField> vals)
+        {
+            int inStartPos = inStream.Position;
+            int outStartPos = outStream.Position;
+
+            byte inHdr = inStream.ReadByte();
+
+            if (inHdr == BinaryUtils.HdrNull)
+                outStream.WriteByte(BinaryUtils.HdrNull);
+            else if (inHdr == BinaryUtils.HdrHnd)
+            {
+                int inHnd = inStream.ReadInt();
+
+                int oldPos = inStartPos - inHnd;
+                int newPos;
+
+                if (ctx.OldToNew(oldPos, out newPos))
+                {
+                    // Handle is still valid.
+                    outStream.WriteByte(BinaryUtils.HdrHnd);
+                    outStream.WriteInt(outStartPos - newPos);
+                }
+                else
+                {
+                    // Handle is invalid, write full object.
+                    int inRetPos = inStream.Position;
+
+                    inStream.Seek(oldPos, SeekOrigin.Begin);
+
+                    Mutate0(ctx, inStream, outStream, false, 0, EmptyVals);
+
+                    inStream.Seek(inRetPos, SeekOrigin.Begin);
+                }
+            }
+            else if (inHdr == BinaryUtils.HdrFull)
+            {
+                var inHeader = BinaryObjectHeader.Read(inStream, inStartPos);
+                
+                BinaryUtils.ValidateProtocolVersion(inHeader.Version);
+
+                int hndPos;
+
+                if (ctx.AddOldToNew(inStartPos, outStartPos, out hndPos))
+                {
+                    // Object could be cached in parent builder.
+                    BinaryBuilderField cachedVal;
+
+                    if (_parent._cache != null && _parent._cache.TryGetValue(inStartPos, out cachedVal))
+                    {
+                        WriteField(ctx, cachedVal);
+                    }
+                    else
+                    {
+                        // New object, write in full form.
+                        var inSchema = inHeader.ReadSchema(inStream, inStartPos);
+
+                        var outSchema = BinaryObjectSchemaHolder.Current;
+                        var schemaIdx = outSchema.PushSchema();
+
+                        try
+                        {
+                            // Skip header as it is not known at this point.
+                            outStream.Seek(BinaryObjectHeader.Size, SeekOrigin.Current);
+
+                            if (inSchema != null)
+                            {
+                                foreach (var inField in inSchema)
+                                {
+                                    BinaryBuilderField fieldVal;
+
+                                    var fieldFound = vals.TryGetValue(inField.Id, out fieldVal);
+
+                                    if (fieldFound && fieldVal == BinaryBuilderField.RmvMarker)
+                                        continue;
+
+                                    outSchema.PushField(inField.Id, outStream.Position - outStartPos);
+
+                                    if (!fieldFound)
+                                        fieldFound = _parent._cache != null &&
+                                                     _parent._cache.TryGetValue(inField.Offset + inStartPos,
+                                                         out fieldVal);
+
+                                    if (fieldFound)
+                                    {
+                                        WriteField(ctx, fieldVal);
+
+                                        vals.Remove(inField.Id);
+                                    }
+                                    else
+                                    {
+                                        // Field is not tracked, re-write as is.
+                                        inStream.Seek(inField.Offset + inStartPos, SeekOrigin.Begin);
+
+                                        Mutate0(ctx, inStream, outStream, false, 0, EmptyVals);
+                                    }
+                                }
+                            }
+
+                            // Write remaining new fields.
+                            foreach (var valEntry in vals)
+                            {
+                                if (valEntry.Value == BinaryBuilderField.RmvMarker)
+                                    continue;
+
+                                outSchema.PushField(valEntry.Key, outStream.Position - outStartPos);
+
+                                WriteField(ctx, valEntry.Value);
+                            }
+
+                            // Write raw data.
+                            int outRawOff = outStream.Position - outStartPos;
+
+                            int inRawOff = inHeader.GetRawOffset(inStream, inStartPos);
+                            int inRawLen = inHeader.SchemaOffset - inRawOff;
+
+                            if (inRawLen > 0)
+                                outStream.Write(inStream.InternalArray, inStartPos + inRawOff, inRawLen);
+
+                            // Write schema
+                            int outSchemaOff = outRawOff;
+                            var schemaPos = outStream.Position;
+                            int outSchemaId;
+                            short flags;
+
+                            var hasSchema = outSchema.WriteSchema(outStream, schemaIdx, out outSchemaId, out flags);
+
+                            if (hasSchema)
+                            {
+                                outSchemaOff = schemaPos - outStartPos;
+
+                                if (inRawLen > 0)
+                                    outStream.WriteInt(outRawOff);
+                            }
+
+                            var outLen = outStream.Position - outStartPos;
+
+                            var outHash = changeHash ? hash : inHeader.HashCode;
+
+                            var outHeader = new BinaryObjectHeader(inHeader.IsUserType, inHeader.TypeId, outHash,
+                                outLen, outSchemaId, outSchemaOff, !hasSchema, flags);
+
+                            BinaryObjectHeader.Write(outHeader, outStream, outStartPos);
+
+                            outStream.Seek(outStartPos + outLen, SeekOrigin.Begin);  // seek to the end of the object
+                        }
+                        finally
+                        {
+                            outSchema.PopSchema(schemaIdx);
+                        }
+                    }
+                }
+                else
+                {
+                    // Object has already been written, write as handle.
+                    outStream.WriteByte(BinaryUtils.HdrHnd);
+                    outStream.WriteInt(outStartPos - hndPos);
+                }
+
+                // Synchronize input stream position.
+                inStream.Seek(inStartPos + inHeader.Length, SeekOrigin.Begin);
+            }
+            else
+            {
+                // Try writing as well-known type with fixed size.
+                outStream.WriteByte(inHdr);
+
+                if (!WriteAsPredefined(inHdr, inStream, outStream, ctx))
+                    throw new IgniteException("Unexpected header [position=" + (inStream.Position - 1) +
+                        ", header=" + inHdr + ']');
+            }
+        }
+
+        /// <summary>
+        /// Writes the specified field.
+        /// </summary>
+        private static void WriteField(Context ctx, BinaryBuilderField field)
+        {
+            var action = field.WriteAction;
+
+            if (action != null)
+                action(ctx.Writer, field.Value);
+            else
+                ctx.Writer.Write(field.Value);
+        }
+
+        /// <summary>
+        /// Process binary object inverting handles if needed.
+        /// </summary>
+        /// <param name="outStream">Output stream.</param>
+        /// <param name="port">Binary object.</param>
+        internal void ProcessBinary(IBinaryStream outStream, BinaryObject port)
+        {
+            // Special case: writing binary object with correct inversions.
+            BinaryHeapStream inStream = new BinaryHeapStream(port.Data);
+
+            inStream.Seek(port.Offset, SeekOrigin.Begin);
+
+            // Use fresh context to ensure correct binary inversion.
+            Mutate0(new Context(), inStream, outStream, false, 0, EmptyVals);
+        }
+
+        /// <summary>
+        /// Process child builder.
+        /// </summary>
+        /// <param name="outStream">Output stream.</param>
+        /// <param name="builder">Builder.</param>
+        internal void ProcessBuilder(IBinaryStream outStream, BinaryObjectBuilder builder)
+        {
+            BinaryHeapStream inStream = new BinaryHeapStream(builder._obj.Data);
+
+            inStream.Seek(builder._obj.Offset, SeekOrigin.Begin);
+
+            // Builder parent context might be null only in one case: if we never met this group of
+            // builders before. In this case we set context to their parent and track it. Context
+            // cleanup will be performed at the very end of build process.
+            if (builder._parent._ctx == null || builder._parent._ctx.Closed)
+                builder._parent._ctx = new Context(_parent._ctx);
+
+            builder.Mutate(inStream, outStream as BinaryHeapStream, builder._desc,
+                    builder._hashCode, builder._vals);
+        }
+
+        /// <summary>
+        /// Write object as a predefined type if possible.
+        /// </summary>
+        /// <param name="hdr">Header.</param>
+        /// <param name="inStream">Input stream.</param>
+        /// <param name="outStream">Output stream.</param>
+        /// <param name="ctx">Context.</param>
+        /// <returns><c>True</c> if was written.</returns>
+        private bool WriteAsPredefined(byte hdr, BinaryHeapStream inStream, IBinaryStream outStream,
+            Context ctx)
+        {
+            switch (hdr)
+            {
+                case BinaryUtils.TypeByte:
+                    TransferBytes(inStream, outStream, 1);
+
+                    break;
+
+                case BinaryUtils.TypeShort:
+                    TransferBytes(inStream, outStream, 2);
+
+                    break;
+
+                case BinaryUtils.TypeInt:
+                    TransferBytes(inStream, outStream, 4);
+
+                    break;
+
+                case BinaryUtils.TypeLong:
+                    TransferBytes(inStream, outStream, 8);
+
+                    break;
+
+                case BinaryUtils.TypeFloat:
+                    TransferBytes(inStream, outStream, 4);
+
+                    break;
+
+                case BinaryUtils.TypeDouble:
+                    TransferBytes(inStream, outStream, 8);
+
+                    break;
+
+                case BinaryUtils.TypeChar:
+                    TransferBytes(inStream, outStream, 2);
+
+                    break;
+
+                case BinaryUtils.TypeBool:
+                    TransferBytes(inStream, outStream, 1);
+
+                    break;
+
+                case BinaryUtils.TypeDecimal:
+                    TransferBytes(inStream, outStream, 4); // Transfer scale
+
+                    int magLen = inStream.ReadInt(); // Transfer magnitude length.
+
+                    outStream.WriteInt(magLen);
+
+                    TransferBytes(inStream, outStream, magLen); // Transfer magnitude.
+
+                    break;
+
+                case BinaryUtils.TypeString:
+                    BinaryUtils.WriteString(BinaryUtils.ReadString(inStream), outStream);
+
+                    break;
+
+                case BinaryUtils.TypeGuid:
+                    TransferBytes(inStream, outStream, 16);
+
+                    break;
+
+                case BinaryUtils.TypeTimestamp:
+                    TransferBytes(inStream, outStream, 12);
+
+                    break;
+
+                case BinaryUtils.TypeArrayByte:
+                    TransferArray(inStream, outStream, 1);
+
+                    break;
+
+                case BinaryUtils.TypeArrayShort:
+                    TransferArray(inStream, outStream, 2);
+
+                    break;
+
+                case BinaryUtils.TypeArrayInt:
+                    TransferArray(inStream, outStream, 4);
+
+                    break;
+
+                case BinaryUtils.TypeArrayLong:
+                    TransferArray(inStream, outStream, 8);
+
+                    break;
+
+                case BinaryUtils.TypeArrayFloat:
+                    TransferArray(inStream, outStream, 4);
+
+                    break;
+
+                case BinaryUtils.TypeArrayDouble:
+                    TransferArray(inStream, outStream, 8);
+
+                    break;
+
+                case BinaryUtils.TypeArrayChar:
+                    TransferArray(inStream, outStream, 2);
+
+                    break;
+
+                case BinaryUtils.TypeArrayBool:
+                    TransferArray(inStream, outStream, 1);
+
+                    break;
+
+                case BinaryUtils.TypeArrayDecimal:
+                case BinaryUtils.TypeArrayString:
+                case BinaryUtils.TypeArrayGuid:
+                case BinaryUtils.TypeArrayTimestamp:
+                case BinaryUtils.TypeArrayEnum:
+                case BinaryUtils.TypeArray:
+                    int arrLen = inStream.ReadInt();
+
+                    outStream.WriteInt(arrLen);
+
+                    for (int i = 0; i < arrLen; i++)
+                        Mutate0(ctx, inStream, outStream, false, 0, null);
+
+                    break;
+
+                case BinaryUtils.TypeCollection:
+                    int colLen = inStream.ReadInt();
+
+                    outStream.WriteInt(colLen);
+
+                    outStream.WriteByte(inStream.ReadByte());
+
+                    for (int i = 0; i < colLen; i++)
+                        Mutate0(ctx, inStream, outStream, false, 0, EmptyVals);
+
+                    break;
+
+                case BinaryUtils.TypeDictionary:
+                    int dictLen = inStream.ReadInt();
+
+                    outStream.WriteInt(dictLen);
+
+                    outStream.WriteByte(inStream.ReadByte());
+
+                    for (int i = 0; i < dictLen; i++)
+                    {
+                        Mutate0(ctx, inStream, outStream, false, 0, EmptyVals);
+                        Mutate0(ctx, inStream, outStream, false, 0, EmptyVals);
+                    }
+
+                    break;
+
+                case BinaryUtils.TypeMapEntry:
+                    Mutate0(ctx, inStream, outStream, false, 0, EmptyVals);
+                    Mutate0(ctx, inStream, outStream, false, 0, EmptyVals);
+
+                    break;
+
+                case BinaryUtils.TypeBinary:
+                    TransferArray(inStream, outStream, 1); // Data array.
+                    TransferBytes(inStream, outStream, 4); // Offset in array.
+
+                    break;
+
+                case BinaryUtils.TypeEnum:
+                    TransferBytes(inStream, outStream, 4); // Integer ordinal.
+
+                    break;
+
+                default:
+                    return false;
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Transfer bytes from one stream to another.
+        /// </summary>
+        /// <param name="inStream">Input stream.</param>
+        /// <param name="outStream">Output stream.</param>
+        /// <param name="cnt">Bytes count.</param>
+        private static void TransferBytes(BinaryHeapStream inStream, IBinaryStream outStream, int cnt)
+        {
+            outStream.Write(inStream.InternalArray, inStream.Position, cnt);
+
+            inStream.Seek(cnt, SeekOrigin.Current);
+        }
+
+        /// <summary>
+        /// Transfer array of fixed-size elements from one stream to another.
+        /// </summary>
+        /// <param name="inStream">Input stream.</param>
+        /// <param name="outStream">Output stream.</param>
+        /// <param name="elemSize">Element size.</param>
+        private static void TransferArray(BinaryHeapStream inStream, IBinaryStream outStream,
+            int elemSize)
+        {
+            int len = inStream.ReadInt();
+
+            outStream.WriteInt(len);
+
+            TransferBytes(inStream, outStream, elemSize * len);
+        }
+
+        /// <summary>
+        /// Mutation ocntext.
+        /// </summary>
+        private class Context
+        {
+            /** Map from object position in old binary to position in new binary. */
+            private IDictionary<int, int> _oldToNew;
+
+            /** Parent context. */
+            private readonly Context _parent;
+
+            /** Binary writer. */
+            private readonly BinaryWriter _writer;
+
+            /** Children contexts. */
+            private ICollection<Context> _children;
+
+            /** Closed flag; if context is closed, it can no longer be used. */
+            private bool _closed;
+
+            /// <summary>
+            /// Constructor for parent context where writer invocation is not expected.
+            /// </summary>
+            public Context()
+            {
+                // No-op.
+            }
+
+            /// <summary>
+            /// Constructor for parent context.
+            /// </summary>
+            /// <param name="writer">Writer</param>
+            public Context(BinaryWriter writer)
+            {
+                _writer = writer;
+            }
+
+            /// <summary>
+            /// Constructor.
+            /// </summary>
+            /// <param name="parent">Parent context.</param>
+            public Context(Context parent)
+            {
+                _parent = parent;
+                
+                _writer = parent._writer;
+
+                if (parent._children == null)
+                    parent._children = new List<Context>();
+
+                parent._children.Add(this);
+            }
+
+            /// <summary>
+            /// Add another old-to-new position mapping.
+            /// </summary>
+            /// <param name="oldPos">Old position.</param>
+            /// <param name="newPos">New position.</param>
+            /// <param name="hndPos">Handle position.</param>
+            /// <returns><c>True</c> if ampping was added, <c>false</c> if mapping already existed and handle
+            /// position in the new object is returned.</returns>
+            public bool AddOldToNew(int oldPos, int newPos, out int hndPos)
+            {
+                if (_oldToNew == null)
+                    _oldToNew = new Dictionary<int, int>();
+
+                if (_oldToNew.TryGetValue(oldPos, out hndPos))
+                    return false;
+                _oldToNew[oldPos] = newPos;
+
+                return true;
+            }
+
+            /// <summary>
+            /// Get mapping of old position to the new one.
+            /// </summary>
+            /// <param name="oldPos">Old position.</param>
+            /// <param name="newPos">New position.</param>
+            /// <returns><c>True</c> if mapping exists.</returns>
+            public bool OldToNew(int oldPos, out int newPos)
+            {
+                return _oldToNew.TryGetValue(oldPos, out newPos);
+            }
+
+            /// <summary>
+            /// Writer.
+            /// </summary>
+            public BinaryWriter Writer
+            {
+                get { return _writer; }
+            }
+
+            /// <summary>
+            /// Closed flag.
+            /// </summary>
+            public bool Closed
+            {
+                get
+                {
+                    return _closed;
+                }
+                set
+                {
+                    Context ctx = this;
+
+                    while (ctx != null)
+                    {
+                        ctx._closed = value;
+
+                        if (_children != null) {
+                            foreach (Context child in _children)
+                                child.Closed = value;
+                        }
+
+                        ctx = ctx._parent;
+                    }
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHandle.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHandle.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHandle.cs
new file mode 100644
index 0000000..35735fe
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHandle.cs
@@ -0,0 +1,59 @@
+/*
+ * 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. Wraps a single value.
+    /// </summary>
+    internal class BinaryObjectHandle
+    {
+        /** Value. */
+        private readonly object _val;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BinaryObjectHandle"/> class.
+        /// </summary>
+        /// <param name="val">The value.</param>
+        public BinaryObjectHandle(object val)
+        {
+            _val = val;
+        }
+
+        /// <summary>
+        /// Gets the value.
+        /// </summary>
+        public object Value
+        {
+            get { return _val; }
+        }
+
+        /** <inheritdoc /> */
+        public override bool Equals(object obj)
+        {
+            var that = obj as BinaryObjectHandle;
+
+            return that != null && _val == that._val;
+        }
+
+        /** <inheritdoc /> */
+        public override int GetHashCode()
+        {
+            return _val != null ? _val.GetHashCode() : 0;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHeader.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHeader.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHeader.cs
new file mode 100644
index 0000000..59cb29c1
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectHeader.cs
@@ -0,0 +1,469 @@
+/*
+ * 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.IO;
+    using System.Runtime.InteropServices;
+    using Apache.Ignite.Core.Impl.Binary.IO;
+
+    /// <summary>
+    /// binary object header structure.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential, Pack = 0)]
+    internal struct BinaryObjectHeader : IEquatable<BinaryObjectHeader>
+    {
+        /** Size, equals to sizeof(BinaryObjectHeader). */
+        public const int Size = 24;
+
+        /** User type flag. */
+        public const short FlagUserType = 0x1;
+
+        /** Raw only flag. */
+        public const short FlagRawOnly = 0x2;
+
+        /** Byte-sized field offsets flag. */
+        public const short FlagByteOffsets = 0x4;
+
+        /** Short-sized field offsets flag. */
+        public const short FlagShortOffsets = 0x8;
+
+        /** Actual header layout */
+        public readonly byte Header;        // Header code, always 103 (HdrFull)
+        public readonly byte Version;       // Protocol version
+        public readonly short Flags;        // Flags
+        public readonly int TypeId;         // Type ID
+        public readonly int HashCode;       // Hash code
+        public readonly int Length;         // Length, including header
+        public readonly int SchemaId;       // Schema ID (Fnv1 of field type ids)
+        public readonly int SchemaOffset;   // Schema offset, or raw offset when RawOnly flag is set.
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BinaryObjectHeader" /> struct.
+        /// </summary>
+        /// <param name="userType">User type flag.</param>
+        /// <param name="typeId">Type ID.</param>
+        /// <param name="hashCode">Hash code.</param>
+        /// <param name="length">Length.</param>
+        /// <param name="schemaId">Schema ID.</param>
+        /// <param name="schemaOffset">Schema offset.</param>
+        /// <param name="rawOnly">Raw flag.</param>
+        /// <param name="flags">The flags.</param>
+        public BinaryObjectHeader(bool userType, int typeId, int hashCode, int length, int schemaId, int schemaOffset, 
+            bool rawOnly, short flags)
+        {
+            Header = BinaryUtils.HdrFull;
+            Version = BinaryUtils.ProtoVer;
+
+            Debug.Assert(schemaOffset <= length);
+            Debug.Assert(schemaOffset >= Size);
+
+            if (userType)
+                flags |= FlagUserType;
+
+            if (rawOnly)
+                flags |= FlagRawOnly;
+
+            Flags = flags;
+
+            TypeId = typeId;
+            HashCode = hashCode;
+            Length = length;
+            SchemaId = schemaId;
+            SchemaOffset = schemaOffset;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BinaryObjectHeader"/> struct from specified stream.
+        /// </summary>
+        /// <param name="stream">The stream.</param>
+        private BinaryObjectHeader(IBinaryStream stream)
+        {
+            Header = stream.ReadByte();
+            Version = stream.ReadByte();
+            Flags = stream.ReadShort();
+            Length = stream.ReadInt();
+            TypeId = stream.ReadInt();
+            HashCode = stream.ReadInt();
+            SchemaId = stream.ReadInt();
+            SchemaOffset = stream.ReadInt();
+        }
+
+        /// <summary>
+        /// Writes this instance to the specified stream.
+        /// </summary>
+        /// <param name="stream">The stream.</param>
+        private void Write(IBinaryStream stream)
+        {
+            stream.WriteByte(Header);
+            stream.WriteByte(Version);
+            stream.WriteShort(Flags);
+            stream.WriteInt(Length);
+            stream.WriteInt(TypeId);
+            stream.WriteInt(HashCode);
+            stream.WriteInt(SchemaId);
+            stream.WriteInt(SchemaOffset);
+        }
+
+        /// <summary>
+        /// Gets a user type flag.
+        /// </summary>
+        public bool IsUserType
+        {
+            get { return (Flags & FlagUserType) == FlagUserType; }
+        }
+
+        /// <summary>
+        /// Gets a raw-only flag.
+        /// </summary>
+        public bool IsRawOnly
+        {
+            get { return (Flags & FlagRawOnly) == FlagRawOnly; }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether this instance has raw offset.
+        /// </summary>
+        public bool HasRawOffset
+        {
+            get
+            {
+                // Remainder => raw offset is the very last 4 bytes in object.
+                return !IsRawOnly && ((Length - SchemaOffset) % SchemaFieldSize) == 4;
+            }
+        }
+
+        /// <summary>
+        /// Gets the size of the schema field offset (1, 2 or 4 bytes).
+        /// </summary>
+        public int SchemaFieldOffsetSize
+        {
+            get
+            {
+                if ((Flags & FlagByteOffsets) == FlagByteOffsets)
+                    return 1;
+
+                if ((Flags & FlagShortOffsets) == FlagShortOffsets)
+                    return 2;
+
+                return 4;
+            }
+        }
+
+        /// <summary>
+        /// Gets the size of the schema field.
+        /// </summary>
+        public int SchemaFieldSize
+        {
+            get { return SchemaFieldOffsetSize + 4; }
+        }
+
+        /// <summary>
+        /// Gets the schema field count.
+        /// </summary>
+        public int SchemaFieldCount
+        {
+            get
+            {
+                if (IsRawOnly)
+                    return 0;
+
+                var schemaSize = Length - SchemaOffset;
+
+                return schemaSize / SchemaFieldSize;
+            }
+        }
+
+        /// <summary>
+        /// Gets the raw offset of this object in specified stream.
+        /// </summary>
+        /// <param name="stream">The stream.</param>
+        /// <param name="position">The position.</param>
+        /// <returns>Raw offset.</returns>
+        public int GetRawOffset(IBinaryStream stream, int position)
+        {
+            Debug.Assert(stream != null);
+
+            if (!HasRawOffset)
+                return SchemaOffset;
+
+            stream.Seek(position + Length - 4, SeekOrigin.Begin);
+
+            return stream.ReadInt();
+        }
+
+        /// <summary>
+        /// Reads the schema as dictionary according to this header data.
+        /// </summary>
+        /// <param name="stream">The stream.</param>
+        /// <param name="position">The position.</param>
+        /// <returns>Schema.</returns>
+        public Dictionary<int, int> ReadSchemaAsDictionary(IBinaryStream stream, int position)
+        {
+            Debug.Assert(stream != null);
+
+            var schemaSize = SchemaFieldCount;
+
+            if (schemaSize == 0)
+                return null;
+
+            stream.Seek(position + SchemaOffset, SeekOrigin.Begin);
+
+            var schema = new Dictionary<int, int>(schemaSize);
+
+            var offsetSize = SchemaFieldOffsetSize;
+
+            if (offsetSize == 1)
+            {
+                for (var i = 0; i < schemaSize; i++)
+                    schema.Add(stream.ReadInt(), stream.ReadByte());
+            }
+            else if (offsetSize == 2)
+            {
+                for (var i = 0; i < schemaSize; i++)
+                    schema.Add(stream.ReadInt(), stream.ReadShort());
+            }
+            else
+            {
+                for (var i = 0; i < schemaSize; i++)
+                    schema.Add(stream.ReadInt(), stream.ReadInt());
+            }
+
+            return schema;
+        }
+
+        /// <summary>
+        /// Reads the schema according to this header data.
+        /// </summary>
+        /// <param name="stream">The stream.</param>
+        /// <param name="position">The position.</param>
+        /// <returns>Schema.</returns>
+        public BinaryObjectSchemaField[] ReadSchema(IBinaryStream stream, int position)
+        {
+            Debug.Assert(stream != null);
+
+            var schemaSize = SchemaFieldCount;
+
+            if (schemaSize == 0)
+                return null;
+
+            stream.Seek(position + SchemaOffset, SeekOrigin.Begin);
+
+            var schema = new BinaryObjectSchemaField[schemaSize];
+
+            var offsetSize = SchemaFieldOffsetSize;
+
+            if (offsetSize == 1)
+            {
+                for (var i = 0; i < schemaSize; i++)
+                    schema[i] = new BinaryObjectSchemaField(stream.ReadInt(), stream.ReadByte());
+            }
+            else if (offsetSize == 2)
+            {
+                for (var i = 0; i < schemaSize; i++)
+                    schema[i] = new BinaryObjectSchemaField(stream.ReadInt(), stream.ReadShort());
+            }
+            else
+            {
+                for (var i = 0; i < schemaSize; i++)
+                    schema[i] = new BinaryObjectSchemaField(stream.ReadInt(), stream.ReadInt());
+            }
+
+            return schema;
+        }
+
+        /// <summary>
+        /// Writes an array of fields to a stream.
+        /// </summary>
+        /// <param name="fields">Fields.</param>
+        /// <param name="stream">Stream.</param>
+        /// <param name="offset">Offset in the array.</param>
+        /// <param name="count">Field count to write.</param>
+        /// <returns>
+        /// Flags according to offset sizes: <see cref="BinaryObjectHeader.FlagByteOffsets" />,
+        /// <see cref="BinaryObjectHeader.FlagShortOffsets" />, or 0.
+        /// </returns>
+        public static unsafe short WriteSchema(BinaryObjectSchemaField[] fields, IBinaryStream stream, int offset,
+            int count)
+        {
+            Debug.Assert(fields != null);
+            Debug.Assert(stream != null);
+            Debug.Assert(count > 0);
+            Debug.Assert(offset >= 0);
+            Debug.Assert(offset < fields.Length);
+
+            unchecked
+            {
+                // Last field is the farthest in the stream
+                var maxFieldOffset = fields[offset + count - 1].Offset;
+
+                if (maxFieldOffset <= byte.MaxValue)
+                {
+                    for (int i = offset; i < count + offset; i++)
+                    {
+                        var field = fields[i];
+
+                        stream.WriteInt(field.Id);
+                        stream.WriteByte((byte)field.Offset);
+                    }
+
+                    return FlagByteOffsets;
+                }
+
+                if (maxFieldOffset <= ushort.MaxValue)
+                {
+                    for (int i = offset; i < count + offset; i++)
+                    {
+                        var field = fields[i];
+
+                        stream.WriteInt(field.Id);
+
+                        stream.WriteShort((short)field.Offset);
+                    }
+
+                    return FlagShortOffsets;
+                }
+
+                if (BitConverter.IsLittleEndian)
+                {
+                    fixed (BinaryObjectSchemaField* ptr = &fields[offset])
+                    {
+                        stream.Write((byte*)ptr, count / BinaryObjectSchemaField.Size);
+                    }
+                }
+                else
+                {
+                    for (int i = offset; i < count + offset; i++)
+                    {
+                        var field = fields[i];
+
+                        stream.WriteInt(field.Id);
+                        stream.WriteInt(field.Offset);
+                    }
+                }
+
+                return 0;
+            }
+
+        }
+
+        /// <summary>
+        /// Writes specified header to a stream.
+        /// </summary>
+        /// <param name="header">The header.</param>
+        /// <param name="stream">The stream.</param>
+        /// <param name="position">The position.</param>
+        public static unsafe void Write(BinaryObjectHeader header, IBinaryStream stream, int position)
+        {
+            Debug.Assert(stream != null);
+            Debug.Assert(position >= 0);
+
+            stream.Seek(position, SeekOrigin.Begin);
+
+            if (BitConverter.IsLittleEndian)
+                stream.Write((byte*) &header, Size);
+            else
+                header.Write(stream);
+        }
+
+        /// <summary>
+        /// Reads an instance from stream.
+        /// </summary>
+        /// <param name="stream">The stream.</param>
+        /// <param name="position">The position.</param>
+        /// <returns>Instance of the header.</returns>
+        public static unsafe BinaryObjectHeader Read(IBinaryStream stream, int position)
+        {
+            Debug.Assert(stream != null);
+            Debug.Assert(position >= 0);
+
+            stream.Seek(position, SeekOrigin.Begin);
+
+            if (BitConverter.IsLittleEndian)
+            {
+                var hdr = new BinaryObjectHeader();
+
+                stream.Read((byte*) &hdr, Size);
+
+                Debug.Assert(hdr.Version == BinaryUtils.ProtoVer);
+                Debug.Assert(hdr.SchemaOffset <= hdr.Length);
+                Debug.Assert(hdr.SchemaOffset >= Size);
+
+                // Only one of the flags can be set
+                var f = hdr.Flags;
+                Debug.Assert((f & (FlagShortOffsets | FlagByteOffsets)) != (FlagShortOffsets | FlagByteOffsets));
+
+                return hdr;
+            }
+
+            return new BinaryObjectHeader(stream);
+        }
+
+        /** <inheritdoc> */
+        public bool Equals(BinaryObjectHeader other)
+        {
+            return Header == other.Header &&
+                   Version == other.Version &&
+                   Flags == other.Flags &&
+                   TypeId == other.TypeId &&
+                   HashCode == other.HashCode &&
+                   Length == other.Length &&
+                   SchemaId == other.SchemaId &&
+                   SchemaOffset == other.SchemaOffset;
+        }
+
+        /** <inheritdoc> */
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj)) return false;
+            
+            return obj is BinaryObjectHeader && Equals((BinaryObjectHeader) obj);
+        }
+
+        /** <inheritdoc> */
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                var hashCode = Header.GetHashCode();
+                hashCode = (hashCode*397) ^ Version.GetHashCode();
+                hashCode = (hashCode*397) ^ Flags.GetHashCode();
+                hashCode = (hashCode*397) ^ TypeId;
+                hashCode = (hashCode*397) ^ HashCode;
+                hashCode = (hashCode*397) ^ Length;
+                hashCode = (hashCode*397) ^ SchemaId;
+                hashCode = (hashCode*397) ^ SchemaOffset;
+                return hashCode;
+            }
+        }
+
+        /** <inheritdoc> */
+        public static bool operator ==(BinaryObjectHeader left, BinaryObjectHeader right)
+        {
+            return left.Equals(right);
+        }
+
+        /** <inheritdoc> */
+        public static bool operator !=(BinaryObjectHeader left, BinaryObjectHeader right)
+        {
+            return !left.Equals(right);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/894057e5/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchema.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchema.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchema.cs
new file mode 100644
index 0000000..a3467b8
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchema.cs
@@ -0,0 +1,98 @@
+/*
+ * 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;
+
+    /// <summary>
+    /// Holds and manages binary object schemas for a specific type.
+    /// </summary>
+    internal class BinaryObjectSchema
+    {
+        /** First schema id. */
+        private volatile int _schemaId1;
+
+        /** First schema. */
+        private volatile int[] _schema1;
+
+        /** Second schema id. */
+        private volatile int _schemaId2;
+
+        /** Second schema. */
+        private volatile int[] _schema2;
+
+        /** Other schemas. */
+        private volatile Dictionary<int, int[]> _schemas;
+
+        /// <summary>
+        /// Gets the schema by id.
+        /// </summary>
+        /// <param name="id">Schema id.</param>
+        /// <returns>Schema or null.</returns>
+        public int[] Get(int id)
+        {
+            if (_schemaId1 == id)
+                return _schema1;
+
+            if (_schemaId2 == id)
+                return _schema2;
+
+            int[] res;
+
+            if (_schemas != null && _schemas.TryGetValue(id, out res))
+                return res;
+
+            return null;
+        }
+
+        /// <summary>
+        /// Adds the schema.
+        /// </summary>
+        /// <param name="id">Schema id.</param>
+        /// <param name="schema">Schema.</param>
+        public void Add(int id, int[] schema)
+        {
+            lock (this)
+            {
+                if (_schemaId1 == id || _schemaId2 == id || (_schemas != null && _schemas.ContainsKey(id)))
+                    return;
+
+                if (_schema1 == null)
+                {
+                    _schemaId1 = id;
+                    _schema1 = schema;
+                }
+                else if (_schema2 == null)
+                {
+                    _schemaId2 = id;
+                    _schema2 = schema;
+                }
+                else
+                {
+                    var schemas = _schemas == null 
+                        ? new Dictionary<int, int[]>() 
+                        : new Dictionary<int, int[]>(_schemas);
+
+                    schemas.Add(id, schema);
+
+                    _schemas = schemas;
+                }
+            }
+        }
+    }
+}
\ 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/BinaryObjectSchemaField.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaField.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaField.cs
new file mode 100644
index 0000000..3c5339a
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaField.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.Runtime.InteropServices;
+
+    /// <summary>
+    /// Binary schema field DTO (as it is stored in a stream).
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential, Pack = 0)]
+    internal struct BinaryObjectSchemaField
+    {
+        /* Field ID */
+        public readonly int Id;
+
+        /** Offset. */
+        public readonly int Offset;
+
+        /** Size, equals to sizeof(BinaryObjectSchemaField) */
+        public const int Size = 8;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="BinaryObjectSchemaField"/> struct.
+        /// </summary>
+        /// <param name="id">The id.</param>
+        /// <param name="offset">The offset.</param>
+        public BinaryObjectSchemaField(int id, int offset)
+        {
+            Id = id;
+            Offset = offset;
+        }
+    }
+}
\ 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/BinaryObjectSchemaHolder.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaHolder.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaHolder.cs
new file mode 100644
index 0000000..75ff2c5
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryObjectSchemaHolder.cs
@@ -0,0 +1,108 @@
+/*
+ * 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.Threading;
+    using Apache.Ignite.Core.Impl.Binary.IO;
+    using Apache.Ignite.Core.Impl.Common;
+
+    /// <summary>
+    /// Shared schema holder.
+    /// </summary>
+    internal class BinaryObjectSchemaHolder
+    {
+        /** Current schema. */
+        private static readonly ThreadLocal<BinaryObjectSchemaHolder> CurrentHolder =
+            new ThreadLocal<BinaryObjectSchemaHolder>(() => new BinaryObjectSchemaHolder());
+
+        /** Fields. */
+        private BinaryObjectSchemaField[] _fields = new BinaryObjectSchemaField[32];
+
+        /** Current field index. */
+        private int _idx;
+
+        /// <summary>
+        /// Gets the schema holder for the current thread.
+        /// </summary>
+        public static BinaryObjectSchemaHolder Current
+        {
+            get { return CurrentHolder.Value; }
+        }
+
+        /// <summary>
+        /// Adds a field to the holder.
+        /// </summary>
+        /// <param name="id">The identifier.</param>
+        /// <param name="offset">The offset.</param>
+        public void PushField(int id, int offset)
+        {
+            if (_idx == _fields.Length)
+                Array.Resize(ref _fields, _fields.Length * 2);
+
+            _fields[_idx] = new BinaryObjectSchemaField(id, offset);
+
+            _idx++;
+        }
+
+        /// <summary>
+        /// Gets the start of a new schema
+        /// </summary>
+        public int PushSchema()
+        {
+            return _idx;
+        }
+
+        /// <summary>
+        /// Resets schema position to specified index.
+        /// </summary>
+        public void PopSchema(int idx)
+        {
+            _idx = idx;
+        }
+
+        /// <summary>
+        /// Writes collected schema to the stream and pops it.
+        /// </summary>
+        /// <param name="stream">The stream.</param>
+        /// <param name="schemaOffset">The schema offset.</param>
+        /// <param name="schemaId">The schema identifier.</param>
+        /// <param name="flags">Flags according to offset sizes: <see cref="BinaryObjectHeader.FlagByteOffsets" />,
+        /// <see cref="BinaryObjectHeader.FlagShortOffsets" />, or 0.</param>
+        /// <returns>
+        /// True if current schema was non empty; false otherwise.
+        /// </returns>
+        public bool WriteSchema(IBinaryStream stream, int schemaOffset, out int schemaId, out short flags)
+        {
+            schemaId = Fnv1Hash.Basis;
+            flags = 0;
+
+            var count = _idx - schemaOffset;
+
+            if (count == 0) 
+                return false;
+
+            flags = BinaryObjectHeader.WriteSchema(_fields, stream, schemaOffset, count);
+
+            for (var i = schemaOffset; i < _idx; i++)
+                schemaId = Fnv1Hash.Update(schemaId, _fields[i].Id);
+
+            return true;
+        }
+    }
+}


Mime
View raw message