//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.Activities.Debugger.Symbol { using System; using System.IO; using System.IO.Compression; using System.Runtime; using System.Runtime.Serialization; using System.Text; using System.Globalization; using System.Collections.Generic; using System.Security.Cryptography; // Represent debug symbol of a workflow tree (similar to pdb file). // It contains the absolute path of the xaml file and the location of each activity in the workflow tree. // This is used to instrument the workflow without having access to the original xaml file. public class WorkflowSymbol { public string FileName { get; set; } public ICollection Symbols { get; set; } private byte[] checksum; public byte[] GetChecksum() { if (this.checksum == null) { return null; } return (byte[]) this.checksum.Clone(); } [Flags] internal enum EncodingFormat : byte { String = 0x76, // Format as well as cookie. String format is hidden from public. Binary = 0x77, Checksum = 0x80 } internal const EncodingFormat DefaultEncodingFormat = EncodingFormat.Binary; public WorkflowSymbol() { } // These constructors are private and used by Decode() method. // Binary deserializer. WorkflowSymbol(BinaryReader reader, byte[] checksum) { this.FileName = reader.ReadString(); int numSymbols = SymbolHelper.ReadEncodedInt32(reader); this.Symbols = new List(numSymbols); for (int i = 0; i < numSymbols; ++i) { this.Symbols.Add(new ActivitySymbol(reader)); } this.checksum = checksum; } // Decode from Base64 string. public static WorkflowSymbol Decode(string symbolString) { byte[] data = Convert.FromBase64String(symbolString); using (BinaryReader reader = new BinaryReader(new MemoryStream(data))) { byte[] checksum = null; EncodingFormat format = (EncodingFormat)reader.ReadByte(); int payloadBytesCount = data.Length - sizeof(EncodingFormat); if (0 != (format & EncodingFormat.Checksum)) { int bytesCount = SymbolHelper.ReadEncodedInt32(reader); checksum = reader.ReadBytes(bytesCount); payloadBytesCount -= SymbolHelper.GetEncodedSize(bytesCount); format &= (~EncodingFormat.Checksum); } switch (format) { case EncodingFormat.Binary: return ParseBinary(reader.ReadBytes(payloadBytesCount), checksum); // Compute the case EncodingFormat.String: return ParseStringRepresentation(reader.ReadString(), checksum); } } throw FxTrace.Exception.AsError(new SerializationException()); } // Serialization // Encode to Base64 string public string Encode() { return Encode(WorkflowSymbol.DefaultEncodingFormat); // default format } internal string Encode(EncodingFormat encodingFormat) { using (MemoryStream ms = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(ms)) { if (this.checksum != null) { writer.Write((byte)(encodingFormat | EncodingFormat.Checksum)); SymbolHelper.WriteEncodedInt32(writer, this.checksum.Length); writer.Write(this.checksum); } else { writer.Write((byte)encodingFormat); } switch (encodingFormat) { case EncodingFormat.Binary: this.Write(writer); break; case EncodingFormat.String: writer.Write(this.ToString()); break; default: throw FxTrace.Exception.AsError(new SerializationException()); } // Need to copy to a buffer to trim excess capacity. byte[] buffer = new byte[ms.Length]; Array.Copy(ms.GetBuffer(), buffer, ms.Length); return Convert.ToBase64String(buffer); } } } // Binary deserializer static WorkflowSymbol ParseBinary(byte[] bytes, byte[] checksum) { using (BinaryReader reader = new BinaryReader(new MemoryStream(bytes))) { return new WorkflowSymbol(reader, checksum); } } // Binary serializer void Write(BinaryWriter writer) { writer.Write(this.FileName ?? string.Empty); if (this.Symbols != null) { SymbolHelper.WriteEncodedInt32(writer, this.Symbols.Count); foreach (ActivitySymbol actSym in this.Symbols) { actSym.Write(writer); } } else { SymbolHelper.WriteEncodedInt32(writer, 0); } } // String encoding serialization. // This is used for String encoding format. public override string ToString() { StringBuilder builder = new StringBuilder(); builder.AppendFormat("{0}", this.FileName ?? string.Empty); if (this.Symbols != null) { foreach (ActivitySymbol symbol in Symbols) { builder.AppendFormat(";{0}", symbol.ToString()); } } return builder.ToString(); } // Deserialization of string encoding format. static WorkflowSymbol ParseStringRepresentation(string symbolString, byte[] checksum) { string[] s = symbolString.Split(';'); int numSymbols = s.Length - 1; ActivitySymbol[] symbols = new ActivitySymbol[numSymbols]; for (int i = 0; i < numSymbols; ++i) { string[] symbolSegments = s[i + 1].Split(','); Fx.Assert(symbolSegments.Length == 5, "Invalid activity symbol"); symbols[i] = new ActivitySymbol { QualifiedId = QualifiedId.Parse(symbolSegments[0]).AsByteArray(), StartLine = int.Parse(symbolSegments[1], CultureInfo.InvariantCulture), StartColumn = int.Parse(symbolSegments[2], CultureInfo.InvariantCulture), EndLine = int.Parse(symbolSegments[3], CultureInfo.InvariantCulture), EndColumn = int.Parse(symbolSegments[4], CultureInfo.InvariantCulture) }; } return new WorkflowSymbol { FileName = s[0], Symbols = symbols, checksum = checksum }; } public bool CalculateChecksum() { this.checksum = null; if (!string.IsNullOrEmpty(this.FileName)) { this.checksum = SymbolHelper.CalculateChecksum(this.FileName); } return (this.checksum != null); } } }