/// <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, PortableHeapStream inStream, IPortableStream outStream, Context ctx) { switch (hdr) { case PortableUtils.TypeByte: TransferBytes(inStream, outStream, 1); break; case PortableUtils.TypeShort: TransferBytes(inStream, outStream, 2); break; case PortableUtils.TypeInt: TransferBytes(inStream, outStream, 4); break; case PortableUtils.TypeLong: TransferBytes(inStream, outStream, 8); break; case PortableUtils.TypeFloat: TransferBytes(inStream, outStream, 4); break; case PortableUtils.TypeDouble: TransferBytes(inStream, outStream, 8); break; case PortableUtils.TypeChar: TransferBytes(inStream, outStream, 2); break; case PortableUtils.TypeBool: TransferBytes(inStream, outStream, 1); break; case PortableUtils.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 PortableUtils.TypeString: PortableUtils.WriteString(PortableUtils.ReadString(inStream), outStream); break; case PortableUtils.TypeGuid: TransferBytes(inStream, outStream, 16); break; case PortableUtils.TypeDate: TransferBytes(inStream, outStream, 12); break; case PortableUtils.TypeArrayByte: TransferArray(inStream, outStream, 1); break; case PortableUtils.TypeArrayShort: TransferArray(inStream, outStream, 2); break; case PortableUtils.TypeArrayInt: TransferArray(inStream, outStream, 4); break; case PortableUtils.TypeArrayLong: TransferArray(inStream, outStream, 8); break; case PortableUtils.TypeArrayFloat: TransferArray(inStream, outStream, 4); break; case PortableUtils.TypeArrayDouble: TransferArray(inStream, outStream, 8); break; case PortableUtils.TypeArrayChar: TransferArray(inStream, outStream, 2); break; case PortableUtils.TypeArrayBool: TransferArray(inStream, outStream, 1); break; case PortableUtils.TypeArrayDecimal: case PortableUtils.TypeArrayString: case PortableUtils.TypeArrayGuid: case PortableUtils.TypeArrayDate: case PortableUtils.TypeArrayEnum: case PortableUtils.TypeArray: int arrLen = inStream.ReadInt(); outStream.WriteInt(arrLen); for (int i = 0; i < arrLen; i++) { Mutate0(ctx, inStream, outStream, false, 0, null); } break; case PortableUtils.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 PortableUtils.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 PortableUtils.TypeMapEntry: Mutate0(ctx, inStream, outStream, false, 0, EmptyVals); Mutate0(ctx, inStream, outStream, false, 0, EmptyVals); break; case PortableUtils.TypePortable: TransferArray(inStream, outStream, 1); // Data array. TransferBytes(inStream, outStream, 4); // Offset in array. break; case PortableUtils.TypeEnum: TransferBytes(inStream, outStream, 4); // Integer ordinal. break; default: return(false); } return(true); }
/// <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, PortableHeapStream inStream, IPortableStream outStream, bool changeHash, int hash, IDictionary <int, object> vals) { int inStartPos = inStream.Position; int outStartPos = outStream.Position; byte inHdr = inStream.ReadByte(); if (inHdr == PortableUtils.HdrNull) { outStream.WriteByte(PortableUtils.HdrNull); } else if (inHdr == PortableUtils.HdrHnd) { int inHnd = inStream.ReadInt(); int oldPos = inStartPos - inHnd; int newPos; if (ctx.OldToNew(oldPos, out newPos)) { // Handle is still valid. outStream.WriteByte(PortableUtils.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 == PortableUtils.HdrFull) { byte inUsrFlag = inStream.ReadByte(); int inTypeId = inStream.ReadInt(); int inHash = inStream.ReadInt(); int inLen = inStream.ReadInt(); int inRawOff = inStream.ReadInt(); int hndPos; if (ctx.AddOldToNew(inStartPos, outStartPos, out hndPos)) { // Object could be cached in parent builder. object cachedVal; if (_parent._cache != null && _parent._cache.TryGetValue(inStartPos, out cachedVal)) { ctx.Writer.Write(cachedVal, null); } else { // New object, write in full form. outStream.WriteByte(PortableUtils.HdrFull); outStream.WriteByte(inUsrFlag); outStream.WriteInt(inTypeId); outStream.WriteInt(changeHash ? hash : inHash); // Skip length and raw offset as they are not known at this point. outStream.Seek(8, SeekOrigin.Current); // Write regular fields. while (inStream.Position < inStartPos + inRawOff) { int inFieldId = inStream.ReadInt(); int inFieldLen = inStream.ReadInt(); int inFieldDataPos = inStream.Position; object fieldVal; bool fieldFound = vals.TryGetValue(inFieldId, out fieldVal); if (!fieldFound || fieldVal != PortableBuilderField.RmvMarkerObj) { outStream.WriteInt(inFieldId); int fieldLenPos = outStream.Position; // Here we will write length later. outStream.Seek(4, SeekOrigin.Current); if (fieldFound) { // Replace field with new value. if (fieldVal != PortableBuilderField.RmvMarkerObj) { ctx.Writer.Write(fieldVal, null); } vals.Remove(inFieldId); } else { // If field was requested earlier, then we must write tracked value if (_parent._cache != null && _parent._cache.TryGetValue(inFieldDataPos, out fieldVal)) { ctx.Writer.Write(fieldVal, null); } else { // Filed is not tracked, re-write as is. Mutate0(ctx, inStream, outStream, false, 0, EmptyVals); } } int fieldEndPos = outStream.Position; outStream.Seek(fieldLenPos, SeekOrigin.Begin); outStream.WriteInt(fieldEndPos - fieldLenPos - 4); outStream.Seek(fieldEndPos, SeekOrigin.Begin); } // Position intput stream pointer after the field. inStream.Seek(inFieldDataPos + inFieldLen, SeekOrigin.Begin); } // Write remaining new fields. foreach (KeyValuePair <int, object> valEntry in vals) { if (valEntry.Value != PortableBuilderField.RmvMarkerObj) { outStream.WriteInt(valEntry.Key); int fieldLenPos = outStream.Position; // Here we will write length later. outStream.Seek(4, SeekOrigin.Current); ctx.Writer.Write(valEntry.Value, null); int fieldEndPos = outStream.Position; outStream.Seek(fieldLenPos, SeekOrigin.Begin); outStream.WriteInt(fieldEndPos - fieldLenPos - 4); outStream.Seek(fieldEndPos, SeekOrigin.Begin); } } // Write raw data. int rawPos = outStream.Position; outStream.Write(inStream.InternalArray, inStartPos + inRawOff, inLen - inRawOff); // Write length and raw data offset. int outResPos = outStream.Position; outStream.Seek(outStartPos + OffsetLen, SeekOrigin.Begin); outStream.WriteInt(outResPos - outStartPos); // Length. outStream.WriteInt(rawPos - outStartPos); // Raw offset. outStream.Seek(outResPos, SeekOrigin.Begin); } } else { // Object has already been written, write as handle. outStream.WriteByte(PortableUtils.HdrHnd); outStream.WriteInt(outStartPos - hndPos); } // Synchronize input stream position. inStream.Seek(inStartPos + inLen, 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 + ']'); } } }