/// <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="vals">Values to be replaced.</param> /// <returns>Mutated object.</returns> private void Mutate0(Context ctx, BinaryHeapStream inStream, IBinaryStream outStream, bool changeHash, 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, 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 = BinaryObjectSchemaSerializer.ReadSchema(inStream, inStartPos, inHeader, _desc.Schema, _binary.Marshaller.Ignite); 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, 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); } var flags = inHeader.IsUserType ? BinaryObjectHeader.Flag.UserType : BinaryObjectHeader.Flag.None; if (inHeader.IsCustomDotNetType) { flags |= BinaryObjectHeader.Flag.CustomDotNetType; } // Write raw data. int outRawOff = outStream.Position - outStartPos; if (inHeader.HasRaw) { var inRawOff = inHeader.GetRawOffset(inStream, inStartPos); var inRawLen = inHeader.SchemaOffset - inRawOff; flags |= BinaryObjectHeader.Flag.HasRaw; outStream.Write(inStream.InternalArray, inStartPos + inRawOff, inRawLen); } // Write schema int outSchemaOff = outRawOff; var schemaPos = outStream.Position; int outSchemaId; if (inHeader.IsCompactFooter) { flags |= BinaryObjectHeader.Flag.CompactFooter; } var hasSchema = outSchema.WriteSchema(outStream, schemaIdx, out outSchemaId, ref flags); if (hasSchema) { outSchemaOff = schemaPos - outStartPos; flags |= BinaryObjectHeader.Flag.HasSchema; if (inHeader.HasRaw) { outStream.WriteInt(outRawOff); } if (_desc.Schema.Get(outSchemaId) == null) { _desc.Schema.Add(outSchemaId, outSchema.GetSchema(schemaIdx)); } } var outLen = outStream.Position - outStartPos; var outHash = inHeader.HashCode; if (changeHash) { // Get from identity resolver. outHash = BinaryArrayEqualityComparer.GetHashCode(outStream, outStartPos + BinaryObjectHeader.Size, schemaPos - outStartPos - BinaryObjectHeader.Size); } var outHeader = new BinaryObjectHeader(inHeader.TypeId, outHash, outLen, outSchemaId, outSchemaOff, 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> /// 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 BinaryTypeId.Byte: TransferBytes(inStream, outStream, 1); break; case BinaryTypeId.Short: TransferBytes(inStream, outStream, 2); break; case BinaryTypeId.Int: TransferBytes(inStream, outStream, 4); break; case BinaryTypeId.Long: TransferBytes(inStream, outStream, 8); break; case BinaryTypeId.Float: TransferBytes(inStream, outStream, 4); break; case BinaryTypeId.Double: TransferBytes(inStream, outStream, 8); break; case BinaryTypeId.Char: TransferBytes(inStream, outStream, 2); break; case BinaryTypeId.Bool: TransferBytes(inStream, outStream, 1); break; case BinaryTypeId.Decimal: TransferBytes(inStream, outStream, 4); // Transfer scale int magLen = inStream.ReadInt(); // Transfer magnitude length. outStream.WriteInt(magLen); TransferBytes(inStream, outStream, magLen); // Transfer magnitude. break; case BinaryTypeId.String: BinaryUtils.WriteString(BinaryUtils.ReadString(inStream), outStream); break; case BinaryTypeId.Guid: TransferBytes(inStream, outStream, 16); break; case BinaryTypeId.Timestamp: TransferBytes(inStream, outStream, 12); break; case BinaryTypeId.ArrayByte: TransferArray(inStream, outStream, 1); break; case BinaryTypeId.ArrayShort: TransferArray(inStream, outStream, 2); break; case BinaryTypeId.ArrayInt: TransferArray(inStream, outStream, 4); break; case BinaryTypeId.ArrayLong: TransferArray(inStream, outStream, 8); break; case BinaryTypeId.ArrayFloat: TransferArray(inStream, outStream, 4); break; case BinaryTypeId.ArrayDouble: TransferArray(inStream, outStream, 8); break; case BinaryTypeId.ArrayChar: TransferArray(inStream, outStream, 2); break; case BinaryTypeId.ArrayBool: TransferArray(inStream, outStream, 1); break; case BinaryTypeId.ArrayDecimal: case BinaryTypeId.ArrayString: case BinaryTypeId.ArrayGuid: case BinaryTypeId.ArrayTimestamp: int arrLen = inStream.ReadInt(); outStream.WriteInt(arrLen); for (int i = 0; i < arrLen; i++) { Mutate0(ctx, inStream, outStream, false, null); } break; case BinaryTypeId.ArrayEnum: case BinaryTypeId.Array: int type = inStream.ReadInt(); outStream.WriteInt(type); if (type == BinaryTypeId.Unregistered) { outStream.WriteByte(inStream.ReadByte()); // String header. BinaryUtils.WriteString(BinaryUtils.ReadString(inStream), outStream); // String data. } arrLen = inStream.ReadInt(); outStream.WriteInt(arrLen); for (int i = 0; i < arrLen; i++) { Mutate0(ctx, inStream, outStream, false, EmptyVals); } break; case BinaryTypeId.Collection: int colLen = inStream.ReadInt(); outStream.WriteInt(colLen); outStream.WriteByte(inStream.ReadByte()); for (int i = 0; i < colLen; i++) { Mutate0(ctx, inStream, outStream, false, EmptyVals); } break; case BinaryTypeId.Dictionary: int dictLen = inStream.ReadInt(); outStream.WriteInt(dictLen); outStream.WriteByte(inStream.ReadByte()); for (int i = 0; i < dictLen; i++) { Mutate0(ctx, inStream, outStream, false, EmptyVals); Mutate0(ctx, inStream, outStream, false, EmptyVals); } break; case BinaryTypeId.Binary: TransferArray(inStream, outStream, 1); // Data array. TransferBytes(inStream, outStream, 4); // Offset in array. break; case BinaryTypeId.Enum: TransferBytes(inStream, outStream, 8); // int typeId, int value. break; default: return(false); } return(true); }
/// <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); }