File: UI\LOSFormatter.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="LOSFormatter.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
#if OBJECTSTATEFORMATTER
 
namespace System.Web.UI {
    using System;
    using System.IO;
    using System.Text;
 
 
    /// <devdoc>
    /// Serializes Web Froms view state. The limited object serialization (LOS) 
    /// formatter is designed for ASCII format serialization. This class
    /// supports serializing any object graph, but is optimized for those containing
    /// strings, arrays, and hashtables. It offers second order optimization for many of
    /// the .NET primitive types.
    /// This class has been replaced with a more optimal serialization mechanism implemented
    /// in LosSerializer. LosFormatter itself uses LosSerialization as part of its
    /// implementation to benefit from the highly compact serialization when possible.
    /// </devdoc>
    public sealed class LosFormatter {
 
        private const int InitialBufferSize = 24;
 
        private ObjectStateFormatter _formatter;
        private bool _enableMac;
 
 
        /// <devdoc>
        ///    <para>Creates a LosFormatter object.</para>
        /// </devdoc>
        public LosFormatter() : this(false, (byte[])null) {
        }
 
 
        /// <devdoc>
        ///    <para>Creates a LosFormatter object, specifying whether view state mac should be
        ///         enabled.  If it is, use macKeyModifier to modify the mac key.</para>
        /// </devdoc>
        public LosFormatter(bool enableMac, string macKeyModifier): this (enableMac, GetBytes(macKeyModifier)) {
        }
 
        public LosFormatter(bool enableMac, byte[] macKeyModifier) {
            _enableMac = enableMac;
            if (enableMac) {
                _formatter = new ObjectStateFormatter(macKeyModifier);
            }
            else {
                _formatter = new ObjectStateFormatter();
            }
        }
 
        private static byte[] GetBytes(string s) {
            if (s != null && s.Length != 0)
                return Encoding.Unicode.GetBytes(s);
            else
                return null;
        }
 
 
        /// <devdoc>
        /// <para> Deserializes a LOS-formatted object from a <see cref='System.IO.Stream'/> object.</para>
        /// </devdoc>
        public object Deserialize(Stream stream) {
            TextReader input = null;
            input = new StreamReader(stream);
            return Deserialize(input);
        }
 
 
        /// <devdoc>
        /// <para>Deserializes a LOS-formatted object from a <see cref='System.IO.TextReader'/> object.</para>
        /// </devdoc>
        public object Deserialize(TextReader input) {
            char[] data = new char[128];
            int read = 0;
            int current = 0;
            int blockSize = InitialBufferSize;
            do {
                read = input.Read(data, current, blockSize);
                current += read;
                if (current > data.Length - blockSize) {
                    char[] bigger = new char[data.Length * 2];
                    Array.Copy(data, bigger, data.Length);
                    data = bigger;
                }
            } while (read == blockSize);
 
            return Deserialize(new String(data, 0, current));
        }
 
 
        /// <devdoc>
        ///    <para>Deserializes a LOS formatted object from a string.</para>
        /// </devdoc>
        public object Deserialize(string input) {
            return _formatter.Deserialize(input);
        }
 
 
        /// <devdoc>
        ///    <para>Serializes the Web Forms view state value into 
        ///       a <see cref='System.IO.Stream'/> object.</para>
        /// </devdoc>
        public void Serialize(Stream stream, object value) {
            TextWriter output = new StreamWriter(stream);
            SerializeInternal(output, value);
            output.Flush();
        }
 
 
        /// <devdoc>
        /// <para>Serializes the view state value into a <see cref='System.IO.TextWriter'/> object.</para>
        /// </devdoc>
        public void Serialize(TextWriter output, object value) {
            SerializeInternal(output, value);
        }
 
 
        /// <devdoc>
        ///     Serialized value into the writer.
        /// </devdoc>
        private void SerializeInternal(TextWriter output, object value) {
            string data = _formatter.Serialize(value);
            output.Write(data);
        }
    }
}
 
#else // !OBJECTSTATEFORMATTER

// uncomment for "human readable" debugging output - no base64 encoding.
//#define NO_BASE64
 
namespace System.Web.UI {
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Runtime.Serialization;
    using System;
    using System.IO;
    using System.Security.Principal;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Diagnostics;
    using System.ComponentModel;
    using System.Globalization;
    using System.Threading;
    using System.Text;
    using System.Web.Configuration;
    using System.Security.Permissions;
 
 
    /// <devdoc>
    ///    <para>Serializes Web Froms view state. The limited object serialization (LOS) 
    ///       formatter is designed for highly compact ASCII format serialization. This class
    ///       supports serializing any object graph, but is optimized for those containing
    ///       strings, arrays, and hashtables. It offers second order optimization for many of
    ///       the .NET primitive types.</para>
    /// </devdoc>
    public sealed class LosFormatter : IStateFormatter {
 
        // NOTE : This formatter is not very fault tolerant, by design. I want
        //      : to avoid a bunch of reduntant checking... Since the format
        //      : is short lived we shouldn't have to worry about it.
        //
 
        // NOTE : Although hex encoding of numbers would be more effecient, it
        //      : would make it much harder to determine what are numbers vs.
        //      : names & types. Unless this becomes a problem, I suggest we
        //      : keep encoding in decimal.
        //
 
        // Known Types. There can only be 50 of these... The order shouldn't
        // matter, we store and index into this array... although there is a
        // slight perf advantage being at the top of the list...
        //
        private static readonly Type[] knownTypes = new Type[]
        {
            typeof(object),
            typeof(System.Web.UI.WebControls.Unit),
            typeof(System.Drawing.Color),
            typeof(System.Int16),
            typeof(System.Int64),
        };
 
        static readonly Encoding EncodingInstance = new UTF8Encoding(false);
        static readonly NumberFormatInfo NumberFormat = NumberFormatInfo.InvariantInfo;
 
        private const int UntypedTypeId = -1;
        private const int NoTypeId = -2;
        private const int InitialBufferSize = 24;
        private const int BufferGrowth = 48;
 
 
        // Constant chars and strings... you can change these and all references to the
        // begin, end, and delimiter chars are fixed up
        private const char leftAngleBracketChar = '<';
        private const char rightAngleBracketChar = '>';
        private const char valueDelimiterChar = ';';
 
        private static readonly char[] escapedCharacters = { leftAngleBracketChar, rightAngleBracketChar, valueDelimiterChar, '\\' };
        private static CharBufferAllocator _charBufferAllocator = new CharBufferAllocator(256, 16);
 
        // reusable Temp buffer used for constructing strings from char arrays... this is
        // more performant than using a StringBuilder.
        //
        private char[] _builder;
        private bool _recyclable;
 
        // Tables used to build up the type and name tables during
        // serialization. Not used during deserilization.
        private IDictionary _typeTable;
 
        // Deserialization variables. Not used during serialization.
        private ArrayList       _deserializedTypeTable;
        private ListDictionary  _deserializedConverterTable;
        private char[]          _deserializationData;
        private int             _current;
 
        // MAC authentication 
        private bool _enableViewStateMac;
        private bool EnableViewStateMac {
            get { return _enableViewStateMac; }
        }
 
        private byte [] _macKey = null;
 
 
        /// <devdoc>
        ///    <para>Creates a LosFormatter object.</para>
        /// </devdoc>
        public LosFormatter() {}
 
 
        /// <devdoc>
        ///    <para>Creates a LosFormatter object, specifying whether view state mac should be
        ///         enabled.  If it is, use macKeyModifier to modify the mac key.</para>
        /// </devdoc>
        public LosFormatter(bool enableMac, string macKeyModifier) {
            _enableViewStateMac = enableMac;
 
            if (macKeyModifier != null)
                _macKey = Encoding.Unicode.GetBytes(macKeyModifier);
        }
 
 
        /// <devdoc>
        /// <para> Deserializes a LOS-formatted object from a <see cref='System.IO.Stream'/> object.</para>
        /// </devdoc>
        public object Deserialize(Stream stream) {
            TextReader input = null;
            input = new StreamReader(stream);
            return Deserialize(input);
        }
 
 
        /// <devdoc>
        /// <para>Deserializes a LOS-formatted object from a <see cref='System.IO.TextReader'/> object.</para>
        /// </devdoc>
        public object Deserialize(TextReader input) {
            char[] data = new char[128];
            int read = 0;
            int current = 0;
            int blockSize = InitialBufferSize;
            do {
                read = input.Read(data, current, blockSize);
                current += read;
                if (current > data.Length - blockSize) {
                    char[] bigger = new char[data.Length * 2];
                    Array.Copy(data, bigger, data.Length);
                    data = bigger;
                }
            } while (read == blockSize);
 
            return Deserialize(new String(data, 0, current));
        }
 
 
        /// <devdoc>
        ///    <para>Deserializes a LOS formatted object from a string.</para>
        /// </devdoc>
        public object Deserialize(string input) {
 
#if NO_BASE64
            char[] data = input.ToCharArray();
#else
            byte[] dataBytes = Convert.FromBase64String(input);
 
            int dataLength = -1;
            if (EnableViewStateMac) {
 
                try {
                    dataBytes = MachineKeySection.GetDecodedData(dataBytes, _macKey, 0, dataBytes.Length, ref dataLength);
                }
                catch (Exception e) {
                    PerfCounters.IncrementCounter(AppPerfCounter.VIEWSTATE_MAC_FAIL);
                    ViewStateException.ThrowMacValidationError(e, input);
                }
            }
 
            if (dataLength == -1) {
                dataLength = dataBytes.Length;
            }
 
            char[] data = EncodingInstance.GetChars(dataBytes, 0, dataLength);
#endif

 
            // clear or allocate name and type tables.
            //
            if (_deserializedTypeTable == null) {
                _deserializedTypeTable = new ArrayList();
                _deserializedConverterTable = new ListDictionary();
            }
            else {
                _deserializedTypeTable.Clear();
                _deserializedConverterTable.Clear();
            }
 
            _builder = (char[])  _charBufferAllocator.GetBuffer();
            _recyclable = true;
 
            // DeserializeValueInternal is recursive, so we just kick this off
            // starting at 0
            _current = 0;
            _deserializationData = data;
            object ret = DeserializeValueInternal();
 
            if (_recyclable)
                _charBufferAllocator.ReuseBuffer(_builder);
            
            return ret;
        }
 
 
        /// <devdoc>
        ///     Deserializes a value from tokens, starting at current. When this
        ///     function returns, current will be left at the next token.
        ///
        ///     This function is recursive.
        /// </devdoc>
        private object DeserializeValueInternal() {
            // Determine the data type... possible combinations are:
            //
            //   @<...>     == array of strings
            //   @T<...>    == array of (typeref T)
            //   b<...>     == base64 encoded value
            //   h<...>     == hashtable
            //   l<...>     == arraylist
            //   p<...>     == pair
            //   t<...>     == triplet
            //   i<...>     == int
            //   o<t/f>     == boolean true/false
            //   T<...>     == (typeref T)
            //   ...        == string 
            //
 
            object value = null;
 
            string token = ConsumeOneToken();
 
            if (_current >= _deserializationData.Length || _deserializationData[_current] != leftAngleBracketChar) {
                // just a string - next token is not a left angle bracket
                // we can shortcut here and just return the string
                //_current++; //consume right angle bracket or delimiter
                return token;
            }
 
            _current++; // consume left angle bracket
 
            // otherwise, we have typeref followed by value
            if (token.Length == 1) {
                // simple type we recognize
                char ch = token[0];
                if (ch == 'p') {
                    Pair p = new Pair();
 
                    if (_deserializationData[_current] != valueDelimiterChar) {
                        p.First = DeserializeValueInternal();
                    }
                    _current++; // consume delimeter
                    if (_deserializationData[_current] != rightAngleBracketChar) {
                        p.Second = DeserializeValueInternal();
                    }
                    value = p;
                }
                else if (ch == 't') {
                    Triplet t = new Triplet();
 
                    if (_deserializationData[_current] != valueDelimiterChar) {
                        t.First = DeserializeValueInternal();
                    }
                    _current++; // consume delimeter
                    if (_deserializationData[_current] != valueDelimiterChar) {
                        t.Second = DeserializeValueInternal();
                    }
                    _current++; // consume delimeter
                    if (_deserializationData[_current] != rightAngleBracketChar) {
                        t.Third = DeserializeValueInternal();
                    }
                    value = t;
                }
                    
                // Parse int32...
                else if (ch == 'i') {
                    value = Int32.Parse(ConsumeOneToken(), NumberFormat);
                }
 
                else if (ch == 'o') {
                    value = _deserializationData[_current] == 't'; 
                    _current++;  // consume t or f
                }
 
                // Parse arrayList...
                //
                else if (ch == 'l') {
                    ArrayList data = new ArrayList();
 
                    while (_deserializationData[_current] != rightAngleBracketChar) {
                        object itemValue = null;
                        if (_deserializationData[_current] != valueDelimiterChar) {
                            itemValue = DeserializeValueInternal();
                        }
                        data.Add(itemValue);
                        _current++; //consume the delimiter
                    }
 
                    value = data;
                }
                else if (ch == '@') {
                    // if we're here, length == 1 so this is a string array
                    value = ConsumeStringArray();
                }
 
                // Parse hashtable...
                //
                else if (ch == 'h') {
                    Hashtable data = new Hashtable();
 
                    while (_deserializationData[_current] != rightAngleBracketChar) {
                        object key;
                        key = DeserializeValueInternal();   // hashtable key cannot be null
 
                        _current++; // consume delimiter
                        if (_deserializationData[_current] != valueDelimiterChar) {
                            data[key] = DeserializeValueInternal();
                        }
                        else {
                            data[key] = null;
                        }
 
                        _current++; // consume delimiter
                    }
 
                    value = data;
                }
 
                // base64 encoded...
                //
                else if (ch == 'b') {
                    string text = ConsumeOneToken();
                    byte[] serializedData;
                    serializedData = Convert.FromBase64String(text);
 
                    if (!String.IsNullOrEmpty(serializedData)) {
                        System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                        value = formatter.Deserialize(new MemoryStream(serializedData));
                    }
                }
 
                // Parse typeconverter value ...
                //
                else {
                    // we have a typeref which is only one character long
                    value = ConsumeTypeConverterValue(token);
                }
 
                
            }
            else {
                // length > 1
 
                // Parse array...
                //
                if (token[0] == '@') {
                    // if we're here, length > 1 so we must have a type ref after the @
                    Type creatableType = TypeFromTypeRef(token.Substring(1));
                    value = ConsumeArray(creatableType);
                }
 
                // Parse typeconverter value ...
                //
                else {
                    // we have a typeref which is more than one character long
                    value = ConsumeTypeConverterValue(token);
                }
            }
 
            _current++; //consume right angle bracket
            return value;
        }
 
 
 
        private string ConsumeOneToken() {
            int locInBuilder = 0;
 
            while (_current < _deserializationData.Length)
            {
                switch (_deserializationData[_current]) {
                    case '\\':
                        _current++; // skip slash
                        if (_deserializationData[_current] == 'e') {
                            _current++;
                            return String.Empty;
                        }
                        _builder[locInBuilder] = _deserializationData[_current];
                        locInBuilder++;
                        break;
                    case valueDelimiterChar:
                    case leftAngleBracketChar:
                    case rightAngleBracketChar:
                        return new string(_builder, 0, locInBuilder);
 
                    default:
                        _builder[locInBuilder] = _deserializationData[_current];
                        locInBuilder++;
                        break;
                }
 
                _current++;
 
                // Alloc _builder always 2 greater than locInBuilder to make sure
                // we can do nested/escape parsing without error...
                //
                if (locInBuilder >= _builder.Length) {
                    char[] bigger = new char[_builder.Length + BufferGrowth];
                    Array.Copy(_builder, bigger, _builder.Length);
                    _builder = bigger;
                    _recyclable = false;
                }
            }
            return new string(_builder, 0, locInBuilder);
        }
 
        private object ConsumeStringArray() {
            ArrayList data = new ArrayList();
            while (_deserializationData[_current] != rightAngleBracketChar) {
                object itemValue = null;
                if (_deserializationData[_current] != valueDelimiterChar) {
                    itemValue = ConsumeOneToken();
                }
                data.Add(itemValue);
                _current++; //consume the delimiter
            }
 
            return data.ToArray(typeof(string));
        }
        
        private object ConsumeArray(Type creatableType) {
            ArrayList data = new ArrayList();
            while (_deserializationData[_current] != rightAngleBracketChar) {
                object itemValue = null;
                if (_deserializationData[_current] != valueDelimiterChar) {
                    itemValue = DeserializeValueInternal();
                }
                data.Add(itemValue);
                _current++; //consume the delimiter
            }
 
            return data.ToArray(creatableType);
        }
 
        private object ConsumeTypeConverterValue(string token) {
            int typeref = ParseNumericString(token);
            TypeConverter tc;
 
            if (typeref != -1) {
                // token is the string representation of the number here
                tc = (TypeConverter) _deserializedConverterTable[token];
                if (tc == null) {
                    // wasn't in the converter table, add it now
                    // we need this case because arrays can add types but not typeconverters
                    Type t = TypeFromTypeCode(typeref);
                    tc = TypeDescriptor.GetConverter(t);
                    _deserializedConverterTable[token] = tc;
                }
            }
            else {
                // it's just a name, lookup type and add to type table
                Type t = Type.GetType(token);
                tc = TypeDescriptor.GetConverter(t);
 
                // add to type table and converter table.
                _deserializedConverterTable[(_deserializedTypeTable.Count + 50).ToString(NumberFormat)] = tc;
                _deserializedTypeTable.Add(t);
            }
            string text = ConsumeOneToken();
            return tc.ConvertFrom(null, CultureInfo.InvariantCulture, text);
        }
 
 
        /// <devdoc>
        ///    <para>Serializes the Web Forms view state value into 
        ///       a <see cref='System.IO.Stream'/> object.</para>
        /// </devdoc>
        public void Serialize(Stream stream, object value) {
            TextWriter output = new StreamWriter(stream);
            SerializeInternal(output, value);
            output.Flush();
        }
 
 
        /// <devdoc>
        /// <para>Serializes the view state value into a <see cref='System.IO.TextWriter'/> object.</para>
        /// </devdoc>
        public void Serialize(TextWriter output, object value) {
            SerializeInternal(output, value);
        }
 
 
        /// <devdoc>
        ///     Serialized value into the writer.
        /// </devdoc>
        private void SerializeInternal(TextWriter output, object value) {
            if (value == null)
                return;
 
            if (_typeTable == null) 
                _typeTable = new HybridDictionary();
            else 
                _typeTable.Clear();
 
#if NO_BASE64
            SerializeValue(output, value);
#else 

            LosWriter writer = new LosWriter();
 
            SerializeValue(writer, value);
 
            writer.CompleteTransforms(output, EnableViewStateMac, _macKey); 
            writer.Dispose();
#endif

        }
 
 
        /// <devdoc>
        ///     Recursively serializes value into the writer.
        /// </devdoc>
        private void SerializeValue(TextWriter output, object value) {
            if (value == null) 
                return;
            
            // First determine the type... either typeless (string), array,
            // typed array, hashtable, pair, triplet, knowntype, typetable reference, or
            // type...
            //
            
            // serialize string...
            //
            if (value is string) {
                WriteEscapedString(output, (string)value);
            }
 
            // serialize Int32...
            //
            else if (value is Int32) {
                output.Write('i');
                output.Write(leftAngleBracketChar);
                output.Write(((Int32)value).ToString(NumberFormat));
                output.Write(rightAngleBracketChar);
            }
            else if (value is Boolean) {
                output.Write('o');
                output.Write(leftAngleBracketChar);
                output.Write( ((bool) value) ? 't' : 'f');
                output.Write(rightAngleBracketChar);
            }
 
            // serialize arraylist...
            //
            else if (value is ArrayList) {
                output.Write('l');
                output.Write(leftAngleBracketChar);
 
                ArrayList ar = (ArrayList)value;
                int c = ar.Count;
                for (int i=0; i<c; i++) {
                    SerializeValue(output, ar[i]);
                    output.Write(valueDelimiterChar);
                }
                output.Write(rightAngleBracketChar);
            }
 
            // serialize hashtable...
            //
            else if (value is Hashtable) {
                output.Write('h');
                output.Write(leftAngleBracketChar);
 
                Hashtable table = (Hashtable)value;
 
                IDictionaryEnumerator e = table.GetEnumerator();
                while (e.MoveNext()) {
                    SerializeValue(output, e.Key);
                    output.Write(valueDelimiterChar);
 
                    SerializeValue(output, e.Value);
                    output.Write(valueDelimiterChar);
                }
                output.Write(rightAngleBracketChar);
            }
 
            else {
                // we'll need the Type object for the last two possibilities
                Type valueType = value.GetType();
                Type strtype = typeof(string);
 
                // serialize Pair
                if (valueType == typeof(Pair)) {
                    Pair p = (Pair) value;
                    output.Write('p');
                    output.Write(leftAngleBracketChar);
 
                    SerializeValue(output, p.First);
                    output.Write(valueDelimiterChar);
                    SerializeValue(output, p.Second);
                    output.Write(rightAngleBracketChar);
                }
 
                // serialize Triplet
                //
                else if (valueType == typeof(Triplet)) {
                    Triplet t = (Triplet) value;
                    output.Write('t');
                    output.Write(leftAngleBracketChar);
 
                    SerializeValue(output, t.First);
                    output.Write(valueDelimiterChar);
                    SerializeValue(output, t.Second);
                    output.Write(valueDelimiterChar);
                    SerializeValue(output, t.Third);
                    output.Write(rightAngleBracketChar);
                }
 
                // serialize array...
                //
                else if (valueType.IsArray) {
                    Type underlyingValueType;
                    underlyingValueType = valueType.GetElementType();
 
                    output.Write('@');
 
                    if (underlyingValueType != strtype) {
                        // write type of array before elements
                        int typeId = GetTypeId(underlyingValueType);
                        WriteTypeId(output, typeId, underlyingValueType);
 
                        output.Write(leftAngleBracketChar);
                        Array ar = (Array)value;
                        for (int i=0; i<ar.Length; i++) {
                            SerializeValue(output, ar.GetValue(i));
                            output.Write(valueDelimiterChar);
                        }
                    }
                    else {
                        // optimization: since we know the underlying values are strings, 
                        // we can skip the recursive call to SerializeValue
                        output.Write(leftAngleBracketChar);
                        string[] ar = (string[])value;
                        for (int i=0; i<ar.Length; i++) {
                            WriteEscapedString(output, ar[i]);
                            output.Write(valueDelimiterChar);
                        }
                    }
                    output.Write(rightAngleBracketChar);
                }
 
                // serialize other value...
                //
                else {
                    int typeId = GetTypeId(valueType);
 
                    // get the type converter 
                    TypeConverter tc = TypeDescriptor.GetConverter(valueType);
 
                    bool toString;
                    bool fromString;
                    if (tc == null || tc is ReferenceConverter) { 
                        toString = false;
                        fromString = false;
                    }
                    else {
                        toString = tc.CanConvertTo(strtype);
                        fromString = tc.CanConvertFrom(strtype);
                    }
                    
                    if (toString && fromString) {
                        //we can convert to and from a string
                        WriteTypeId(output, typeId, valueType);
                        
                        output.Write(leftAngleBracketChar);
                        WriteEscapedString(output, tc.ConvertToInvariantString(null, value));
                    }
                    else {
                        // the typeconverter failed us, so we are resorting to binary serialization
                        MemoryStream ms = new MemoryStream();
                        System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
                        try {
                            formatter.Serialize(ms, value);
                        }
                        catch (SerializationException) {
                            throw new HttpException(SR.GetString(SR.NonSerializableType, value.GetType().FullName));
                        }
 
                        output.Write('b');
                        output.Write(leftAngleBracketChar);
 
                        // Since base64 doesn't have any chars that we escape, we can
                        // skip WriteEscapedString
                        output.Write(Convert.ToBase64String(ms.GetBuffer(), 0, (int) ms.Length));
                    }
                    output.Write(rightAngleBracketChar);
                }
            }
        }
 
        private int GetTypeId(Type valueType) {
 
            int typeId = NoTypeId;
 
            // check if it is a known type
            for (int i=0; i<knownTypes.Length; i++) {
                if (valueType == knownTypes[i]) {
                    typeId = i;
                    break;
                }
            }
 
 
            if (typeId == NoTypeId) {
                // not a known type, see if it's in the type table
                object found = _typeTable[valueType];
                if (found != null) 
                    typeId = 50 + (int)found;
            }
 
            return typeId;
        }
 
        private void WriteTypeId(TextWriter output, int typeId, Type valueType) {
            if (typeId != NoTypeId)
                output.Write(typeId.ToString(NumberFormat));
            else {
                // ASURT 60173: use AssemblyQualifiedName here, not FullName
                WriteEscapedString(output, valueType.AssemblyQualifiedName);
                // 
                _typeTable[valueType] = _typeTable.Count;
            }
        }
 
 
 
        /// <devdoc>
        ///     Takes a typeRef, and converts it to a Type. Either by returning
        ///     Type.GetType(typeRef), or looking it up.
        /// </devdoc>
        private Type TypeFromTypeRef(string typeRef) {
 
            int number = ParseNumericString(typeRef);
 
            Type t = TypeFromTypeCode(number);
 
            if (t != null)
                return t;
 
            // it's just a name, lookup type and add to type table
            t = Type.GetType(typeRef);
            _deserializedTypeTable.Add(t);
            return t;
 
        }
 
        private Type TypeFromTypeCode(int number) {
            if (number != -1) {
                // it is a type id, either in the known table or in our type table
                if (number <= 49)
                    return knownTypes[number];
 
                return (Type) _deserializedTypeTable[number - 50];
 
            }
            return null;
        }
 
 
        // Note : We have to determine if "typeRef" is a number. The easiest
        //      : and fastest way to do this is to walk the string. While
        //      : we are doing this, lets build up the number... after
        //      : all this is much faster than Int32.Parse
        //
        private int ParseNumericString(string num) {
            int number = 0;
            int len = num.Length;
 
            for (int i=0; i<len; i++) {
                switch (num[i]) {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        number *= 10;
                        number += (((int)num[i]) - ((int)'0'));
                        break;
                    default:
                        number = -1;
                        i = len;
                        break;
                }
            }
            return number;
        }
 
 
        /// <devdoc>
        ///     Escapes and writes the escaped value of str into the writer.
        /// </devdoc>
        private void WriteEscapedString(TextWriter output, string str) {
 
            if (str == null)
                return;
            
            // need to "escape" the empty string to distinguish it
            // from a null value
            if (str.Length == 0) {
                output.Write('\\');
                output.Write('e');
                return;
            }
 
            int first = str.IndexOfAny(escapedCharacters);
            if (first == -1) {
                output.Write(str);
            }
            else {
                char[] strData = str.ToCharArray();
                output.Write(strData, 0, first);
                int len = strData.Length;
 
                for (int i=first; i<len; i++) {
                    char c = strData[i];
                    switch (c) {
                        case '\\':
                            output.Write('\\');
                            output.Write('\\');
                            break;
                        case leftAngleBracketChar:
                            output.Write('\\');
                            output.Write(leftAngleBracketChar);
                            break;
                        case rightAngleBracketChar:
                            output.Write('\\');
                            output.Write(rightAngleBracketChar);
                            break;
                        case valueDelimiterChar:
                            output.Write('\\');
                            output.Write(valueDelimiterChar);
                            break;
                        default:
                            output.Write(c);
                            break;
                    }
                }
            }
        }
 
        static internal int EstimateSize(object obj) {
            if (obj == null)
                return 0;
            StringWriter sw = new StringWriter();
            LosFormatter formatter = new LosFormatter();
            formatter.Serialize(sw, obj);
            return sw.ToString().Length;
        }
 
        #region Implementation of IStateFormatter
        object IStateFormatter.Deserialize(string serializedState) {
            return Deserialize(serializedState);
        }
 
        string IStateFormatter.Serialize(object state) {
            StringWriter writer = new StringWriter();
            Serialize(writer, state);
            return writer.ToString();
        }
        #endregion
    }
}
 
#endif // OBJECTSTATEFORMATTER