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