ObjectStateFormatter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / UI / ObjectStateFormatter.cs / 1305376 / ObjectStateFormatter.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web.UI { 
 
    using System;
    using System.Collections; 
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Drawing; 
    using System.IO;
    using System.Globalization; 
    using System.Reflection; 
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary; 
    using System.Security;
    using System.Security.Permissions;
    using System.Text;
    using System.Web.Compilation; 
    using System.Web.Configuration;
    using System.Web.Util; 
    using System.Web.Management; 
    using System.Web.UI.WebControls;
 
    //


 
    /// 
    /// ObjectStateFormatter is designed to efficiently serialize arbitrary object graphs 
    /// that represent the state of an object (decomposed into simpler types) into 
    /// a highly compact binary or ASCII representations.
    /// The formatter contains native support for optimized serialization of a fixed 
    /// set of known types such as ints, shorts, booleans, strings, other primitive types
    /// arrays, Pairs, Triplets, ArrayLists, Hashtables etc. In addition it utilizes
    /// TypeConverters for semi-optimized serialization of custom types. Finally, it uses
    /// binary serialization as a fallback mechanism. The formatter is also able to compress 
    /// IndexedStrings contained in the object graph.
    ///  
    public sealed class ObjectStateFormatter : IStateFormatter, IFormatter { 

        // Optimized type tokens 
        private const byte Token_Int16 = 1;
        private const byte Token_Int32 = 2;
        private const byte Token_Byte = 3;
        private const byte Token_Char = 4; 
        private const byte Token_String = 5;
        private const byte Token_DateTime = 6; 
        private const byte Token_Double = 7; 
        private const byte Token_Single = 8;
        private const byte Token_Color = 9; 
        private const byte Token_KnownColor = 10;
        private const byte Token_IntEnum = 11;
        private const byte Token_EmptyColor = 12;
        private const byte Token_Pair = 15; 
        private const byte Token_Triplet = 16;
        private const byte Token_Array = 20; 
        private const byte Token_StringArray = 21; 
        private const byte Token_ArrayList = 22;
        private const byte Token_Hashtable = 23; 
        private const byte Token_HybridDictionary = 24;
        private const byte Token_Type = 25;
        // private const byte Token_Nullable = 26; Removed per DevDiv 165426
        // Background: Used to support nullables as a special case, CLR added support for this 
        // but they forgot to remove the deserialization code when they removed the support
        // potentially Beta2 customers could have serialized data (WebParts) which have this token. 
        // We removed support since this was broken anyways in RTM. 
        private const byte Token_Unit = 27;
        private const byte Token_EmptyUnit = 28; 

        // String-table optimized strings
        private const byte Token_IndexedStringAdd = 30;
        private const byte Token_IndexedString = 31; 

        // Semi-optimized (TypeConverter-based) 
        private const byte Token_StringFormatted = 40; 

        // Semi-optimized (Types) 
        private const byte Token_TypeRefAdd = 41;
        private const byte Token_TypeRefAddLocal = 42;
        private const byte Token_TypeRef = 43;
 
        // Un-optimized (Binary serialized) types
        private const byte Token_BinarySerialized = 50; 
 
        // Optimized for sparse arrays
        private const byte Token_SparseArray = 60; 

        // Constant values
        private const byte Token_Null = 100;
        private const byte Token_EmptyString = 101; 
        private const byte Token_ZeroInt32 = 102;
        private const byte Token_True = 103; 
        private const byte Token_False = 104; 

        // Known types for which we generate short type references 
        // rather than assembly qualified names
        //

 
        private static readonly Type[] KnownTypes =
            new Type[] { 
                typeof(object), 
                typeof(int),
                typeof(string), 
                typeof(bool)
            };

        private static readonly Stack _streams = new Stack(); 

        // Format and Version 
        private const byte Marker_Format = 0xFF; 
        private const byte Marker_Version_1 = 0x01;
 
        // The size of the string table. At most it can be Byte.MaxValue.
        //
        private const int StringTableSize = Byte.MaxValue;
 
        // Used during serialization
        private IDictionary _typeTable; 
        private IDictionary _stringTable; 

        // Used during deserialization 
        private IList _typeList;

        // Used during both serialization and deserialization
        private int _stringTableCount; 
        private string[] _stringList;
 
        // Used for performing Mac-encoding when this LosSerializer is used 
        // in view state serialization.
        private byte[] _macKeyBytes; 

        // If true, this class will throw an exception if it cannot deserialize a type or value.
        // If false, this class will use insert "null" if it cannot deserialize a type or value.
        // Default is true, WebParts Personalization sets this to false. 
        private bool _throwOnErrorDeserializing;
 
        // We use page to determine whether to to encrypt or decrypt based on Page.RequiresViewStateEncryptionInternal or Page.ContainsEncryptedViewstate 
        private Page _page;
 
        /// 
        /// Initializes a new instance of the ObjectStateFormatter.
        /// 
        public ObjectStateFormatter() : this(null) { 
        }
 
        ///  
        /// 
        /// Initializes a new instance of the ObjectStateFormatter. A MAC encoding 
        /// key can be specified to have the serialized data encoded for view state
        /// purposes.
        /// NOTE: this constructor is mainly for LOSFormatter's consumption, not used internally
        ///  
        internal ObjectStateFormatter(byte[] macEncodingKey) : this(null, true) {
            _macKeyBytes = macEncodingKey; 
        } 

        ///  
        /// 
        /// Initializes a new instance of the ObjectStateFormatter. A MAC encoding
        /// key can be specified to have the serialized data encoded for view state
        /// purposes. The Page object is used to determine whether the viewstate will be encrypted 
        /// for serialize and deserialize.
        ///  
 
        internal ObjectStateFormatter(Page page, bool throwOnErrorDeserializing) {
            _page = page; 
            _throwOnErrorDeserializing = throwOnErrorDeserializing;
        }

        // This will return the MacKeyModifier provided in the LOSFormatter constructor or 
        // generate one from Page if EnableViewStateMac is true.
        private byte[] GetMacKeyModifier() { 
            if (_macKeyBytes == null) { 
                // Only generate a MacKeyModifier if we have a page
                if (_page == null) { 
                    return null;
                }

                // Note: duplicated in MobilePage.cs, keep in [....] 

                // Use the page's directory and class name as part of the key (ASURT 64044) 
                // We need to make sure that the hash is case insensitive, since the file system 
                // is, and strange view state errors could otherwise happen (ASURT 128657)
                int pageHashCode = StringComparer.InvariantCultureIgnoreCase.GetHashCode( 
                    _page.TemplateSourceDirectory);
                pageHashCode += StringComparer.InvariantCultureIgnoreCase.GetHashCode(_page.GetType().Name);

                string viewStateUserKey = _page.ViewStateUserKey; 
                if (viewStateUserKey != null) {
                    // Modify the key with the ViewStateUserKey, if any (ASURT 126375) 
                    int count = Encoding.Unicode.GetByteCount(viewStateUserKey); 
                    _macKeyBytes = new byte[count + 4];
                    Encoding.Unicode.GetBytes(viewStateUserKey, 0, viewStateUserKey.Length, _macKeyBytes, 4); 

                }
                else {
                    _macKeyBytes = new byte[4]; 
                }
 
                _macKeyBytes[0] = (byte)pageHashCode; 
                _macKeyBytes[1] = (byte)(pageHashCode >> 8);
                _macKeyBytes[2] = (byte)(pageHashCode >> 16); 
                _macKeyBytes[3] = (byte)(pageHashCode >> 24);
            }
            return _macKeyBytes;
        } 

        ///  
        /// Adds a string reference during the deserialization process 
        /// to support deserialization of IndexedStrings.
        /// The string is added to the string list on the fly, so it is available 
        /// for future reference by index.
        /// 
        private void AddDeserializationStringReference(string s) {
            Debug.Assert((s != null) && (s.Length != 0)); 

            if (_stringTableCount == StringTableSize) { 
                // loop around to the start of the table 
                _stringTableCount = 0;
            } 

            _stringList[_stringTableCount] = s;
            _stringTableCount++;
        } 

        ///  
        /// Adds a type reference during the deserialization process, 
        /// so that it can be referred to later by its index.
        ///  
        private void AddDeserializationTypeReference(Type type) {
            // Type may be null, if there is no longer a Type on the system with the saved type name.
            // This is unlikely to happen with a Type stored in ViewState, but more likely with a Type
            // stored in Personalization. 
            _typeList.Add(type);
        } 
 
        /// 
        /// Adds a string reference during the serialization process to support 
        /// the serialization of IndexedStrings.
        /// The string is added to the string list, as well as to a string table
        /// for quick lookup.
        ///  
        private void AddSerializationStringReference(string s) {
            Debug.Assert((s != null) && (s.Length != 0)); 
 
            if (_stringTableCount == StringTableSize) {
                // loop around to the start of the table 
                _stringTableCount = 0;
            }

            string oldString = _stringList[_stringTableCount]; 
            if (oldString != null) {
                // it means we're looping around, and the existing table entry 
                // needs to be removed, as a new one will replace it 
                Debug.Assert(_stringTable.Contains(oldString));
                _stringTable.Remove(oldString); 
            }

            _stringTable[s] = _stringTableCount;
            _stringList[_stringTableCount] = s; 
            _stringTableCount++;
        } 
 
        /// 
        /// Adds a type reference during the serialization process, so it 
        /// can be later referred to by its index.
        /// 
        private void AddSerializationTypeReference(Type type) {
            Debug.Assert(type != null); 

            int typeID = _typeTable.Count; 
            _typeTable[type] = typeID; 
        }
 
        [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.SerializationFormatter)]
        internal object DeserializeWithAssert(Stream inputStream) {
            return Deserialize(inputStream);
        } 

        ///  
        /// Deserializes an object graph from its binary serialized form 
        /// contained in the specified stream.
        ///  
        public object Deserialize(Stream inputStream) {
            if (inputStream == null) {
                throw new ArgumentNullException("inputStream");
            } 

            Exception deserializationException = null; 
 
            InitializeDeserializer();
 
            SerializerBinaryReader reader = new SerializerBinaryReader(inputStream);
            try {
                byte formatMarker = reader.ReadByte();
 
                if (formatMarker == Marker_Format) {
                    byte versionMarker = reader.ReadByte(); 
 
                    Debug.Assert(versionMarker == Marker_Version_1);
                    if (versionMarker == Marker_Version_1) { 
                        return DeserializeValue(reader);
                    }
                }
            } 
            catch (Exception e) {
                deserializationException = e; 
            } 

            // throw an exception if there was an exception during deserialization 
            // or if deserialization was skipped because of invalid format or
            // version data in the stream

            throw new ArgumentException(SR.GetString(SR.InvalidSerializedData), deserializationException); 
        }
 
 
        /// 
        /// Deserializes an object graph from its textual serialized form 
        /// contained in the specified string.
        /// 
        public object Deserialize(string inputString) {
            if (String.IsNullOrEmpty(inputString)) { 
                throw new ArgumentNullException("inputString");
            } 
 
            byte[] inputBytes = Convert.FromBase64String(inputString);
            int length = inputBytes.Length; 

#if !FEATURE_PAL // FEATURE_PAL does not enable cryptography
            try {
                if (_page != null && _page.ContainsEncryptedViewState) { 
                    inputBytes = MachineKeySection.EncryptOrDecryptData(false, inputBytes, GetMacKeyModifier(), 0, length);
                    length = inputBytes.Length; 
                } 
                // We need to decode if the page has EnableViewStateMac or we got passed in some mac key string
                else if ((_page != null && _page.EnableViewStateMac) || _macKeyBytes != null) { 
                    inputBytes = MachineKeySection.GetDecodedData(inputBytes, GetMacKeyModifier(), 0, length, ref length);
                }
            }
            catch (Exception e) { 
                PerfCounters.IncrementCounter(AppPerfCounter.VIEWSTATE_MAC_FAIL);
                ViewStateException.ThrowMacValidationError(e, inputString); 
            } 
#endif // !FEATURE_PAL
            object result = null; 
            MemoryStream objectStream = GetMemoryStream();
            try {
                objectStream.Write(inputBytes, 0, length);
                objectStream.Position = 0; 
                result = Deserialize(objectStream);
            } 
            finally { 
                ReleaseMemoryStream(objectStream);
            } 
            return result;
        }

        ///  
        /// Deserializes an IndexedString. An IndexedString can either be the string itself (the
        /// first occurrence), or a reference to it by index into the string table. 
        ///  
        private IndexedString DeserializeIndexedString(SerializerBinaryReader reader, byte token) {
            Debug.Assert((token == Token_IndexedStringAdd) || (token == Token_IndexedString)); 

            if (token == Token_IndexedString) {
                // reference to string in the current string table
                int tableIndex = (int)reader.ReadByte(); 

                Debug.Assert(_stringList[tableIndex] != null); 
                return new IndexedString(_stringList[tableIndex]); 
            }
            else { 
                // first occurrence of this indexed string. Read in the string, and add
                // a reference to it, so future references can be resolved.
                string s = reader.ReadString();
 
                AddDeserializationStringReference(s);
                return new IndexedString(s); 
            } 
        }
 
        /// 
        /// Deserializes a Type. A Type can either be its name (the first occurrence),
        /// or a reference to it by index into the type table.  If we cannot load the type,
        /// we throw an exception if _throwOnErrorDeserializing is true, and we return null if 
        /// _throwOnErrorDeserializing is false.
        ///  
        private Type DeserializeType(SerializerBinaryReader reader) { 
            byte token = reader.ReadByte();
            Debug.Assert((token == Token_TypeRef) || 
                         (token == Token_TypeRefAdd) ||
                         (token == Token_TypeRefAddLocal));

            if (token == Token_TypeRef) { 
                // reference by index into type table
                int typeID = reader.ReadEncodedInt32(); 
                return (Type)_typeList[typeID]; 
            }
            else { 
                // first occurrence of this type. Read in the type, resolve it, and
                // add it to the type table
                string typeName = reader.ReadString();
 
                Type resolvedType = null;
                try { 
                    if (token == Token_TypeRefAddLocal) { 
                        resolvedType = HttpContext.SystemWebAssembly.GetType(typeName, true);
                    } 
                    else {
                        resolvedType = Type.GetType(typeName, true);
                    }
                } 
                catch (Exception exception) {
                    if (_throwOnErrorDeserializing) { 
                        throw; 
                    }
                    else { 
                        // Log error message
                        WebBaseEvent.RaiseSystemEvent(
                            SR.GetString(SR.Webevent_msg_OSF_Deserialization_Type, typeName),
                            this, 
                            WebEventCodes.WebErrorObjectStateFormatterDeserializationError,
                            WebEventCodes.UndefinedEventDetailCode, 
                            exception); 
                    }
                } 

                AddDeserializationTypeReference(resolvedType);
                return resolvedType;
            } 
        }
 
        ///  
        /// Deserializes a single value from the underlying stream.
        /// Essentially a token is read, followed by as much data needed to recreate 
        /// the single value.
        /// 
        private object DeserializeValue(SerializerBinaryReader reader) {
            byte token = reader.ReadByte(); 

            // NOTE: Preserve the order here with the order of the logic in 
            //       the SerializeValue method. 

            switch (token) { 
                case Token_Null:
                    return null;
                case Token_EmptyString:
                    return String.Empty; 
                case Token_String:
                    return reader.ReadString(); 
                case Token_ZeroInt32: 
                    return 0;
                case Token_Int32: 
                    return reader.ReadEncodedInt32();
                case Token_Pair:
                    return new Pair(DeserializeValue(reader),
                                    DeserializeValue(reader)); 
                case Token_Triplet:
                    return new Triplet(DeserializeValue(reader), 
                                       DeserializeValue(reader), 
                                       DeserializeValue(reader));
                case Token_IndexedString: 
                case Token_IndexedStringAdd:
                    return DeserializeIndexedString(reader, token);
                case Token_ArrayList:
                    { 
                        int count = reader.ReadEncodedInt32();
                        ArrayList list = new ArrayList(count); 
                        for (int i = 0; i < count; i++) { 
                            list.Add(DeserializeValue(reader));
                        } 

                        return list;
                    }
                case Token_True: 
                    return true;
                case Token_False: 
                    return false; 
                case Token_Byte:
                    return reader.ReadByte(); 
                case Token_Char:
                    return reader.ReadChar();
                case Token_DateTime:
                    return DateTime.FromBinary(reader.ReadInt64()); 
                case Token_Double:
                    return reader.ReadDouble(); 
                case Token_Int16: 
                    return reader.ReadInt16();
                case Token_Single: 
                    return reader.ReadSingle();
                case Token_Hashtable:
                case Token_HybridDictionary:
                    { 
                        int count = reader.ReadEncodedInt32();
 
                        IDictionary table; 
                        if (token == Token_Hashtable) {
                            table = new Hashtable(count); 
                        }
                        else {
                            table = new HybridDictionary(count);
                        } 
                        for (int i = 0; i < count; i++) {
                            table.Add(DeserializeValue(reader), 
                                      DeserializeValue(reader)); 
                        }
 
                        return table;
                    }
                case Token_Type:
                    return DeserializeType(reader); 
                case Token_StringArray:
                    { 
                        int count = reader.ReadEncodedInt32(); 

                        string[] array = new string[count]; 
                        for (int i = 0; i < count; i++) {
                            array[i] = reader.ReadString();
                        }
 
                        return array;
                    } 
                case Token_Array: 
                    {
                        Type elementType = DeserializeType(reader); 
                        int count = reader.ReadEncodedInt32();

                        Array list = Array.CreateInstance(elementType, count);
                        for (int i = 0; i < count; i++) { 
                            list.SetValue(DeserializeValue(reader), i);
                        } 
 
                        return list;
                    } 
                case Token_IntEnum:
                    {
                        Type enumType = DeserializeType(reader);
                        int enumValue = reader.ReadEncodedInt32(); 

                        return Enum.ToObject(enumType, enumValue); 
                    } 
                case Token_Color:
                    return Color.FromArgb(reader.ReadInt32()); 
                case Token_EmptyColor:
                    return Color.Empty;
                case Token_KnownColor:
                    return Color.FromKnownColor((KnownColor)reader.ReadEncodedInt32()); 
                case Token_Unit:
                    return new Unit(reader.ReadDouble(), (UnitType)reader.ReadInt32()); 
                case Token_EmptyUnit: 
                    return Unit.Empty;
                case Token_SparseArray: 
                    {
                        Type elementType = DeserializeType(reader);
                        int count = reader.ReadEncodedInt32();
                        int itemCount = reader.ReadEncodedInt32(); 

                        // Guard against bad data 
                        if (itemCount > count) { 
                            throw new InvalidOperationException(SR.GetString(SR.InvalidSerializedData));
                        } 

                        Array list = Array.CreateInstance(elementType, count);
                        for (int i = 0; i < itemCount; ++i) {
                            // Data is encoded as  
                            int nextPos = reader.ReadEncodedInt32();
 
                            // Guard against bad data (nextPos way too big, or nextPos not increasing) 
                            if (nextPos >= count || nextPos < 0) {
                                throw new InvalidOperationException(SR.GetString(SR.InvalidSerializedData)); 
                            }
                            list.SetValue(DeserializeValue(reader), nextPos);
                        }
 
                        return list;
                    } 
                case Token_StringFormatted: 
                    {
                        object result = null; 

                        Type valueType = DeserializeType(reader);
                        string formattedValue = reader.ReadString();
 
                        if (valueType != null) {
                            TypeConverter converter = TypeDescriptor.GetConverter(valueType); 
                            // TypeDescriptor.GetConverter() will never return null.  The ref docs 
                            // for this method are incorrect.
                            try { 
                                result = converter.ConvertFromInvariantString(formattedValue);
                            }
                            catch (Exception exception) {
                                if (_throwOnErrorDeserializing) { 
                                    throw;
                                } 
                                else { 
                                    WebBaseEvent.RaiseSystemEvent(
                                        SR.GetString(SR.Webevent_msg_OSF_Deserialization_String, valueType.AssemblyQualifiedName), 
                                        this,
                                        WebEventCodes.WebErrorObjectStateFormatterDeserializationError,
                                        WebEventCodes.UndefinedEventDetailCode,
                                        exception); 
                                }
                            } 
                        } 

                        return result; 
                    }
                case Token_BinarySerialized:
                    {
                        int length = reader.ReadEncodedInt32(); 

                        byte[] buffer = new byte[length]; 
                        if (length != 0) { 
                            reader.Read(buffer, 0, length);
                        } 

                        object result = null;
                        MemoryStream ms = GetMemoryStream();
                        try { 
                            ms.Write(buffer, 0, length);
                            ms.Position = 0; 
                            IFormatter formatter = new BinaryFormatter(); 

                            result = formatter.Deserialize(ms); 
                        }
                        catch (Exception exception) {
                            if (_throwOnErrorDeserializing) {
                                throw; 
                            }
                            else { 
                                WebBaseEvent.RaiseSystemEvent( 
                                    SR.GetString(SR.Webevent_msg_OSF_Deserialization_Binary),
                                    this, 
                                    WebEventCodes.WebErrorObjectStateFormatterDeserializationError,
                                    WebEventCodes.UndefinedEventDetailCode,
                                    exception);
                            } 
                        }
                        finally { 
                            ReleaseMemoryStream(ms); 
                        }
                        return result; 
                    }
                default:
                    throw new InvalidOperationException(SR.GetString(SR.InvalidSerializedData));
            } 
        }
 
        ///  
        /// Retrieves a memory stream from the pool.
        ///  
        private static MemoryStream GetMemoryStream() {
            MemoryStream stream = null;
            if (_streams.Count > 0) {
                lock (_streams) { 
                    if (_streams.Count > 0) {
                        stream = (MemoryStream)_streams.Pop(); 
                    } 
                }
            } 

            if (stream == null) {
                stream = new MemoryStream(2048);
            } 
            return stream;
        } 
 

        ///  
        /// Initializes this instance to perform deserialization.
        /// 
        private void InitializeDeserializer() {
            _typeList = new ArrayList(); 

            for (int i = 0; i < KnownTypes.Length; i++) { 
                AddDeserializationTypeReference(KnownTypes[i]); 
            }
 
            _stringList = new string[Byte.MaxValue];
            _stringTableCount = 0;
        }
 
        /// 
        /// Initializes this instance to perform serialization. 
        ///  
        private void InitializeSerializer() {
            _typeTable = new HybridDictionary(); 

            for (int i = 0; i < KnownTypes.Length; i++) {
                AddSerializationTypeReference(KnownTypes[i]);
            } 

            _stringList = new string[Byte.MaxValue]; 
            _stringTable = new Hashtable(); 
            _stringTableCount = 0;
        } 

        /// 
        /// Returns a memory stream back into the pool.
        ///  
        private static void ReleaseMemoryStream(MemoryStream stream) {
            Debug.Assert(stream != null); 
            stream.Position = 0; 
            stream.SetLength(0);
            lock (_streams) { 
                _streams.Push(stream);
            }
        }
 
        /// 
        /// Serializes an object graph into a textual serialized form. 
        ///  
        public string Serialize(object stateGraph) {
            string result = null; 

            MemoryStream ms = GetMemoryStream();
            try {
                Serialize(ms, stateGraph); 
                ms.SetLength(ms.Position);
 
                byte[] buffer = ms.GetBuffer(); 
                int length = (int)ms.Length;
 
#if !FEATURE_PAL // FEATURE_PAL does not enable cryptography
                // We only support serialization of encrypted or encoded data through our internal Page constructors
                if (_page != null && _page.RequiresViewStateEncryptionInternal) {
                    buffer = MachineKeySection.EncryptOrDecryptData(true, buffer, GetMacKeyModifier(), 0, length); 
                    length = buffer.Length;
                } 
                // We need to encode if the page has EnableViewStateMac or we got passed in some mac key string 
                else if ((_page != null && _page.EnableViewStateMac) || _macKeyBytes != null) {
                    buffer = MachineKeySection.GetEncodedData(buffer, GetMacKeyModifier(), 0, ref length); 
                }

#endif // !FEATURE_PAL
                result = Convert.ToBase64String(buffer, 0, length); 
            }
            finally { 
                ReleaseMemoryStream(ms); 
            }
            return result; 
        }

        [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.SerializationFormatter)]
        internal void SerializeWithAssert(Stream outputStream, object stateGraph) { 
            Serialize(outputStream, stateGraph);
        } 
 
        /// 
        /// Serializes an object graph into a binary serialized form within 
        /// the specified stream.
        /// 
        public void Serialize(Stream outputStream, object stateGraph) {
            if (outputStream == null) { 
                throw new ArgumentNullException("outputStream");
            } 
 
            InitializeSerializer();
 
            SerializerBinaryWriter writer = new SerializerBinaryWriter(outputStream);
            writer.Write(Marker_Format);
            writer.Write(Marker_Version_1);
            SerializeValue(writer, stateGraph); 
        }
 
        ///  
        /// Serializes an IndexedString. If this is the first occurrence, it is written
        /// out to the underlying stream, and is added to the string table for future 
        /// reference. Otherwise, a reference by index is written out.
        /// 
        private void SerializeIndexedString(SerializerBinaryWriter writer, string s) {
            object id = _stringTable[s]; 
            if (id != null) {
                writer.Write(Token_IndexedString); 
                writer.Write((byte)(int)id); 
                return;
            } 

            AddSerializationStringReference(s);

            writer.Write(Token_IndexedStringAdd); 
            writer.Write(s);
        } 
 
        /// 
        /// Serializes a Type. If this is the first occurrence, the type name is written 
        /// out to the underlying stream, and the type is added to the string table for future
        /// reference. Otherwise, a reference by index is written out.
        /// 
        private void SerializeType(SerializerBinaryWriter writer, Type type) { 
            object id = _typeTable[type];
            if (id != null) { 
                writer.Write(Token_TypeRef); 
                writer.WriteEncoded((int)id);
                return; 
            }

            AddSerializationTypeReference(type);
 
            if (type.Assembly == HttpContext.SystemWebAssembly) {
                writer.Write(Token_TypeRefAddLocal); 
                writer.Write(type.FullName); 
            }
            else { 
                writer.Write(Token_TypeRefAdd);
                writer.Write(type.AssemblyQualifiedName);
            }
        } 

        ///  
        /// Serializes a single value using the specified writer. 
        /// Handles exceptions to provide more information about the value being serialized.
        ///  
        private void SerializeValue(SerializerBinaryWriter writer, object value) {
            try {

                Stack objectStack = new Stack(); 
                objectStack.Push(value);
 
                do { 
                    value = objectStack.Pop();
 
                    if (value == null) {
                        writer.Write(Token_Null);
                        continue;
                    } 

                    // NOTE: These are ordered roughly in the order of frequency. 
 
                    if (value is string) {
                        string s = (string)value; 
                        if (s.Length == 0) {
                            writer.Write(Token_EmptyString);
                        }
                        else { 
                            writer.Write(Token_String);
                            writer.Write(s); 
                        } 
                        continue;
                    } 

                    if (value is int) {
                        int i = (int)value;
                        if (i == 0) { 
                            writer.Write(Token_ZeroInt32);
                        } 
                        else { 
                            writer.Write(Token_Int32);
                            writer.WriteEncoded(i); 
                        }
                        continue;
                    }
 
                    if (value is Pair) {
                        writer.Write(Token_Pair); 
 
                        Pair p = (Pair)value;
                        objectStack.Push(p.Second); 
                        objectStack.Push(p.First);
                        continue;
                    }
 
                    if (value is Triplet) {
                        writer.Write(Token_Triplet); 
 
                        Triplet t = (Triplet)value;
                        objectStack.Push(t.Third); 
                        objectStack.Push(t.Second);
                        objectStack.Push(t.First);
                        continue;
                    } 

                    if (value is IndexedString) { 
                        Debug.Assert(((IndexedString)value).Value != null); 
                        SerializeIndexedString(writer, ((IndexedString)value).Value);
                        continue; 
                    }

                    if (value.GetType() == typeof(ArrayList)) {
                        writer.Write(Token_ArrayList); 

                        ArrayList list = (ArrayList)value; 
 
                        writer.WriteEncoded(list.Count);
                        for (int i = list.Count - 1; i >= 0; i--) { 
                            objectStack.Push(list[i]);
                        }

                        continue; 
                    }
 
                    if (value is bool) { 
                        if (((bool)value)) {
                            writer.Write(Token_True); 
                        }
                        else {
                            writer.Write(Token_False);
                        } 
                        continue;
                    } 
                    if (value is byte) { 
                        writer.Write(Token_Byte);
                        writer.Write((byte)value); 
                        continue;
                    }
                    if (value is char) {
                        writer.Write(Token_Char); 
                        writer.Write((char)value);
                        continue; 
                    } 
                    if (value is DateTime) {
                        writer.Write(Token_DateTime); 
                        writer.Write(((DateTime)value).ToBinary());
                        continue;
                    }
                    if (value is double) { 
                        writer.Write(Token_Double);
                        writer.Write((double)value); 
                        continue; 
                    }
                    if (value is short) { 
                        writer.Write(Token_Int16);
                        writer.Write((short)value);
                        continue;
                    } 
                    if (value is float) {
                        writer.Write(Token_Single); 
                        writer.Write((float)value); 
                        continue;
                    } 

                    if (value is IDictionary) {
                        bool canSerializeDictionary = false;
 
                        if (value.GetType() == typeof(Hashtable)) {
                            writer.Write(Token_Hashtable); 
                            canSerializeDictionary = true; 
                        }
                        else if (value.GetType() == typeof(HybridDictionary)) { 
                            writer.Write(Token_HybridDictionary);
                            canSerializeDictionary = true;
                        }
 
                        if (canSerializeDictionary) {
                            IDictionary table = (IDictionary)value; 
 
                            writer.WriteEncoded(table.Count);
                            if (table.Count != 0) { 
                                foreach (DictionaryEntry entry in table) {
                                    objectStack.Push(entry.Value);
                                    objectStack.Push(entry.Key);
                                } 
                            }
 
                            continue; 
                        }
                    } 

                    if (value is Type) {
                        writer.Write(Token_Type);
                        SerializeType(writer, (Type)value); 
                        continue;
                    } 
 
                    Type valueType = value.GetType();
 
                    if (value is Array) {
                        // We only support Arrays with rank 1 (No multi dimensional arrays
                        if (((Array)value).Rank > 1) {
                            continue; 
                        }
 
                        Type underlyingType = valueType.GetElementType(); 

                        if (underlyingType == typeof(string)) { 
                            string[] strings = (string[])value;
                            bool containsNulls = false;
                            for (int i = 0; i < strings.Length; i++) {
                                if (strings[i] == null) { 
                                    // Will have to treat these as generic arrays since we
                                    // can't represent nulls in the binary stream, without 
                                    // writing out string token markers. 
                                    // Generic array writing includes the token markers.
                                    containsNulls = true; 
                                    break;
                                }
                            }
 
                            if (!containsNulls) {
                                writer.Write(Token_StringArray); 
                                writer.WriteEncoded(strings.Length); 
                                for (int i = 0; i < strings.Length; i++) {
                                    writer.Write(strings[i]); 
                                }
                                continue;
                            }
                        } 

                        Array values = (Array)value; 
 
                        // Optimize for sparse arrays, if the array is more than 3/4 nulls
                        if (values.Length > 3) { 
                            int sparseThreshold = (values.Length / 4) + 1;
                            int numValues = 0;
                            List items = new List(sparseThreshold);
                            for (int i = 0; i < values.Length; ++i) { 
                                if (values.GetValue(i) != null) {
                                    ++numValues; 
                                    if (numValues >= sparseThreshold) { 
                                        break;
                                    } 
                                    items.Add(i);
                                }
                            }
 
                            // We have enough nulls to use sparse array format 
                            if (numValues < sparseThreshold) { 
                                writer.Write(Token_SparseArray); 
                                SerializeType(writer, underlyingType);
 
                                writer.WriteEncoded(values.Length);
                                writer.WriteEncoded(numValues);

                                // Now we need to just serialize pairs representing the index, and the item 
                                foreach (int index in items) {
                                    writer.WriteEncoded(index); 
                                    SerializeValue(writer, values.GetValue(index)); 
                                }
 
                                continue;
                            }
                        }
 
                        writer.Write(Token_Array);
                        SerializeType(writer, underlyingType); 
 
                        writer.WriteEncoded(values.Length);
                        for (int i = values.Length - 1; i >= 0; i--) { 
                            objectStack.Push(values.GetValue(i));
                        }

                        continue; 
                    }
 
                    if (valueType.IsEnum) { 
                        Type underlyingType = Enum.GetUnderlyingType(valueType);
                        if (underlyingType == typeof(int)) { 
                            writer.Write(Token_IntEnum);
                            SerializeType(writer, valueType);
                            writer.WriteEncoded((int)value);
 
                            continue;
                        } 
                    } 

                    if (valueType == typeof(Color)) { 
                        Color c = (Color)value;
                        if (c.IsEmpty) {
                            writer.Write(Token_EmptyColor);
                            continue; 
                        }
                        if (!c.IsNamedColor) { 
                            writer.Write(Token_Color); 
                            writer.Write(c.ToArgb());
                            continue; 
                        }
                        else {
                            writer.Write(Token_KnownColor);
                            writer.WriteEncoded((int)c.ToKnownColor()); 
                            continue;
                        } 
                    } 

                    if (value is Unit) { 
                        Unit uval = (Unit)value;
                        if (uval.IsEmpty) {
                            writer.Write(Token_EmptyUnit);
                        } 
                        else {
                            writer.Write(Token_Unit); 
                            writer.Write(uval.Value); 
                            writer.Write((int)uval.Type);
                        } 
                        continue;
                    }

                    // Handle the remaining types 
                    // First try to get a type converter, and then resort to
                    // binary serialization if all else fails 
 
                    TypeConverter converter = TypeDescriptor.GetConverter(valueType);
                    bool canConvert = System.Web.UI.Util.CanConvertToFrom(converter, typeof(string)); 

                    if (canConvert) {
                        writer.Write(Token_StringFormatted);
                        SerializeType(writer, valueType); 
                        writer.Write(converter.ConvertToInvariantString(null, value));
                    } 
                    else { 
                        IFormatter formatter = new BinaryFormatter();
                        MemoryStream ms = new MemoryStream(256); 
                        formatter.Serialize(ms, value);

                        byte[] buffer = ms.GetBuffer();
                        int length = (int)ms.Length; 

                        writer.Write(Token_BinarySerialized); 
                        writer.WriteEncoded(length); 
                        if (buffer.Length != 0) {
                            writer.Write(buffer, 0, (int)length); 
                        }
                    }
                }
                while (objectStack.Count > 0); 
            }
            catch (Exception serializationException) { 
                throw new ArgumentException(SR.GetString(SR.ErrorSerializingValue, value.ToString(), value.GetType().FullName), 
                                            serializationException);
            } 
        }

        #region Implementation of IStateFormatter
        object IStateFormatter.Deserialize(string serializedState) { 
            return Deserialize(serializedState);
        } 
 
        string IStateFormatter.Serialize(object state) {
            return Serialize(state); 
        }
        #endregion

        #region Implementation of IFormatter 

        ///  
        SerializationBinder IFormatter.Binder { 
            get {
                return null; 
            }
            set {
            }
        } 

 
        ///  
        StreamingContext IFormatter.Context {
            get { 
                return new StreamingContext(StreamingContextStates.All);
            }
            set {
            } 
        }
 
 
        /// 
        ISurrogateSelector IFormatter.SurrogateSelector { 
            get {
                return null;
            }
            set { 
            }
        } 
 

        ///  
        object IFormatter.Deserialize(Stream serializationStream) {
            return Deserialize(serializationStream);
        }
 

        ///  
        void IFormatter.Serialize(Stream serializationStream, object stateGraph) { 
            Serialize(serializationStream, stateGraph);
        } 
        #endregion

        /// 
        /// Custom BinaryReader used during the deserialization. 
        /// 
        private sealed class SerializerBinaryReader : BinaryReader { 
 
            public SerializerBinaryReader(Stream stream) : base(stream) {
            } 

            public int ReadEncodedInt32() {
                return Read7BitEncodedInt();
            } 
        }
 
 
        /// 
        /// Custom BinaryWriter used during the serialization. 
        /// 
        private sealed class SerializerBinaryWriter : BinaryWriter {

            public SerializerBinaryWriter(Stream stream) : base(stream) { 
            }
 
            public void WriteEncoded(int value) { 
                //
 
                uint v = (uint)value;
                while (v >= 0x80) {
                    Write((byte)(v | 0x80));
                    v >>= 7; 
                }
                Write((byte)v); 
            } 
        }
    } 
}


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK