Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / xsp / System / Web / UI / ObjectStateFormatter.cs / 1 / 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. /// [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)] 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; 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 sync // 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; } // MethodInfo for Nullable.FromObject() private static MethodInfo NullableFromObject = typeof(Nullable).GetMethod("FromObject"); private static object BuildNullableObject(object value, Type type) { // Convert from valuetype stored in value to Nullable // by calling Nullable .FromObject() Debug.Assert(type.IsValueType, "Expected type parameter in Nullable to be a value type"); // MethodInfo boundNullableFromObject = NullableFromObject.MakeGenericMethod(type); return boundNullableFromObject.Invoke(null, new object[] { value }); } [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 asint 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_Nullable: Type T = DeserializeType(reader); object value = DeserializeValue(reader); return BuildNullableObject(value, T); 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; Listitems = 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); } } } }
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- odbcmetadatacollectionnames.cs
- MsmqProcessProtocolHandler.cs
- Codec.cs
- DuplexChannel.cs
- DataGridViewCellToolTipTextNeededEventArgs.cs
- DeviceContext.cs
- ValueTypeFixupInfo.cs
- WorkflowOperationInvoker.cs
- ObjectSecurityT.cs
- WebPartVerb.cs
- DataGridColumnCollectionEditor.cs
- DomainConstraint.cs
- FixedSOMTable.cs
- MeshGeometry3D.cs
- MulticastOption.cs
- PeerNameRegistration.cs
- XpsFixedPageReaderWriter.cs
- CodeMemberMethod.cs
- TokenBasedSetEnumerator.cs
- ProjectionCamera.cs
- RuleSetReference.cs
- Delegate.cs
- ClientOperation.cs
- FileDetails.cs
- PrimitiveSchema.cs
- SecurityElement.cs
- MsdtcWrapper.cs
- VerificationException.cs
- PropertyEmitterBase.cs
- DataGridDesigner.cs
- MDIClient.cs
- HMACSHA384.cs
- ContextBase.cs
- SourceFileBuildProvider.cs
- BasicExpressionVisitor.cs
- Activator.cs
- LOSFormatter.cs
- Point3DAnimationBase.cs
- DefaultSection.cs
- PrefixHandle.cs
- SqlXmlStorage.cs
- ListViewUpdateEventArgs.cs
- PropertyGridDesigner.cs
- SchemaConstraints.cs
- BehaviorEditorPart.cs
- GlobalItem.cs
- _AcceptOverlappedAsyncResult.cs
- PeerNameRegistration.cs
- EpmCustomContentWriterNodeData.cs
- ControlEvent.cs
- CodeCommentStatement.cs
- formatter.cs
- BeginCreateSecurityTokenRequest.cs
- MediaElementAutomationPeer.cs
- HandlerFactoryCache.cs
- COM2TypeInfoProcessor.cs
- ServiceContractViewControl.cs
- ColorAnimation.cs
- ViewBase.cs
- XPathPatternParser.cs
- EnumConverter.cs
- ExtendedProtectionPolicy.cs
- SerializationInfoEnumerator.cs
- GridItemPattern.cs
- EndpointConfigContainer.cs
- SpecialNameAttribute.cs
- DataServiceStreamProviderWrapper.cs
- ErrorFormatterPage.cs
- FunctionCommandText.cs
- AbstractExpressions.cs
- ExpressionPrefixAttribute.cs
- PerformanceCounterManager.cs
- ClientApiGenerator.cs
- Ray3DHitTestResult.cs
- LongValidator.cs
- ColumnBinding.cs
- TypeUsage.cs
- ListItemCollection.cs
- FormViewUpdatedEventArgs.cs
- OdbcConnectionString.cs
- DbSetClause.cs
- SchemaAttDef.cs
- _BufferOffsetSize.cs
- InvalidCastException.cs
- ClientFormsAuthenticationCredentials.cs
- Drawing.cs
- FileSystemInfo.cs
- XmlILAnnotation.cs
- WebPartTransformerCollection.cs
- DataGridItemAutomationPeer.cs
- PropertyGeneratedEventArgs.cs
- ObjectSet.cs
- XmlSortKeyAccumulator.cs
- StreamInfo.cs
- DataGridViewToolTip.cs
- SmtpSpecifiedPickupDirectoryElement.cs
- WebReferencesBuildProvider.cs
- EndPoint.cs
- ActivitiesCollection.cs
- XmlAutoDetectWriter.cs