UriExt.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 / Net / System / UriExt.cs / 1305376 / UriExt.cs

                            /*++ 
Copyright (c) 2003 Microsoft Corporation

Module Name:
 
    UriExt.cs
 
Abstract: 

    Uri extensibility model Implementation. 
    This file utilizes partial class feature.
    Uri.cs file contains core System.Uri functionality.

Author: 
    Alexei Vopilov    Nov 21 2003
 
Revision History: 

--*/ 

namespace System {
    using System.Collections.Generic;
    using System.Configuration; 
    using System.Globalization;
    using System.Net.Configuration; 
    using System.Security.Permissions; 
    using System.Text;
    using System.Runtime.InteropServices; 

    public partial class Uri {
        //
        // All public ctors go through here 
        //
        private void CreateThis(string uri, bool dontEscape, UriKind uriKind) 
        { 
            // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow to be used here.
            if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative) { 
                throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind));
            }

            m_String = uri == null? string.Empty: uri; 

            if (dontEscape) 
                m_Flags |= Flags.UserEscaped; 

            ParsingError err = ParseScheme(m_String, ref m_Flags, ref m_Syntax); 
            UriFormatException e;

            InitializeUri(err, uriKind, out e);
            if (e != null) 
                throw e;
        } 
        // 
        private void InitializeUri(ParsingError err, UriKind uriKind, out UriFormatException e)
        { 
            if (err == ParsingError.None)
            {
                if (IsImplicitFile)
                { 
                    // V1 compat VsWhidbey#252282
                    // A relative Uri wins over implicit UNC path unless the UNC path is of the form "\\something" and uriKind != Absolute 
                    if ( 
#if !PLATFORM_UNIX
                        NotAny(Flags.DosPath) && 
#endif // !PLATFORM_UNIX
                        uriKind != UriKind.Absolute &&
                       (uriKind == UriKind.Relative || (m_String.Length >= 2 && (m_String[0] != '\\' || m_String[1] != '\\'))))
 
                    {
                        m_Syntax = null; //make it be relative Uri 
                        m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri 
                        e = null;
                        return; 
                        // Otheriwse an absolute file Uri wins when it's of the form "\\something"
                    }
                    //
                    // VsWhidbey#423805 and V1 compat issue 
                    // We should support relative Uris of the form c:\bla or c:/bla
                    // 
#if !PLATFORM_UNIX 
                    else if (uriKind == UriKind.Relative && InFact(Flags.DosPath))
                    { 
                        m_Syntax = null; //make it be relative Uri
                        m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
                        e = null;
                        return; 
                        // Otheriwse an absolute file Uri wins when it's of the form "c:\something"
                    } 
#endif // !PLATFORM_UNIX 
                }
            } 
            else if (err > ParsingError.LastRelativeUriOkErrIndex)
            {
                //This is a fatal error based solely on scheme name parsing
                m_String = null; // make it be invalid Uri 
                e = GetException(err);
                return; 
            } 

            System.Net.GlobalLog.Assert(err == ParsingError.None || (object)m_Syntax == null, "Uri::.ctor|ParseScheme has found an error:{0}, but m_Syntax is not null.", err); 
            //
            //
            //
            bool hasUnicode = false; 

            // Is there unicode .. 
            if ((!s_ConfigInitialized) && CheckForConfigLoad(m_String)){ 
                InitializeUriConfig();
            } 

            m_iriParsing = (s_IriParsing && ((m_Syntax == null) || m_Syntax.InFact(UriSyntaxFlags.AllowIriParsing)));

            if (m_iriParsing && CheckForUnicode(m_String)){ 
                m_Flags |= Flags.HasUnicode;
                hasUnicode = true; 
                // switch internal strings 
                m_originalUnicodeString = m_String; // original string location changed
            } 

            if (m_Syntax != null)
            {
                if (m_Syntax.IsSimple) 
                {
                    if ((err = PrivateParseMinimal()) != ParsingError.None) 
                    { 
                        if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex)
                        { 
                            // RFC 3986 Section 5.4.2 - http:(relativeUri) may be considered a valid relative Uri.
                            m_Syntax = null; // convert to relative uri
                            e = null;
                            m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri 
                        }
                        else 
                            e = GetException(err); 
                    }
                    else if (uriKind == UriKind.Relative) 
                    {
                        // Here we know that we can create an absolute Uri, but the user has requested only a relative one
                        e = GetException(ParsingError.CannotCreateRelative);
                    } 
                    else
                        e = null; 
                    // will return from here 

                    if (m_iriParsing && hasUnicode){ 
                        // In this scenario we need to parse the whole string
                        EnsureParseRemaining();
                    }
                } 
                else
                { 
                    // offer custom parser to create a parsing context 
                    m_Syntax = m_Syntax.InternalOnNewUri();
 
                    // incase they won't call us
                    m_Flags |= Flags.UserDrivenParsing;

                    // Ask a registered type to validate this uri 
                    m_Syntax.InternalValidate(this, out e);
 
                    if (e != null) 
                    {
                        // Can we still take it as a relative Uri? 
                        if (uriKind != UriKind.Absolute && err != ParsingError.None && err <= ParsingError.LastRelativeUriOkErrIndex)
                        {
                            m_Syntax = null; // convert it to relative
                            e = null; 
                            m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
                        } 
                    } 
                    else // e == null
                    { 
                        if (err != ParsingError.None || InFact(Flags.ErrorOrParsingRecursion))
                        {
                            // User parser took over on an invalid Uri
                            SetUserDrivenParsing(); 
                        }
                        else if (uriKind == UriKind.Relative) 
                        { 
                            // Here we know that custom parser can create an absolute Uri, but the user has requested only a relative one
                            e = GetException(ParsingError.CannotCreateRelative); 
                        }

                        if (m_iriParsing && hasUnicode){
                            // In this scenario we need to parse the whole string 
                            EnsureParseRemaining();
                        } 
 
                    }
                    // will return from here 
                }
            }
            // If we encountered any parsing errors that indicate this may be a relative Uri,
            // and we'll allow relative Uri's, then create one. 
            else if (err != ParsingError.None && uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex)
            { 
                e = null; 
                m_Flags &= (Flags.UserEscaped | Flags.HasUnicode); // the only flags that makes sense for a relative uri
                if (m_iriParsing && hasUnicode) 
                {
                    // Iri'ze and then normalize relative uris
                    m_String = EscapeUnescapeIri(m_originalUnicodeString, 0, m_originalUnicodeString.Length,
                                                (UriComponents)0); 
                    try
                    { 
                        m_String = m_String.Normalize(NormalizationForm.FormC); 
                    }
                    catch (ArgumentException) 
                    {
                        e = GetException(ParsingError.BadFormat);
                    }
                } 
            }
            else 
            { 
               m_String = null; // make it be invalid Uri
               e = GetException(err); 
            }
        }

        // 
        // http://www.ietf.org/rfc/rfc3986.txt
        // 
        // 3.3.  Path 
        // In addition, a URI reference (Section 4.1) may be a relative-path reference, in which case the  first
        // path segment cannot contain a colon (":") character. 
        //
        // 4.2.  Relative Reference
        // A path segment that contains a colon character (e.g., "this:that") cannot be used as the first segment
        // of a relative-path reference, as it would be mistaken for a scheme name.  Such a segment must be 
        // preceded by a dot-segment (e.g., "./this:that") to make a relative-path reference.
        // 
        // 5.4.2. Abnormal Examples 
        // http:(relativeUri) may be considered a valid relative Uri.
        // 
        // Returns true if a colon is found in the first path segment, false otherwise
        //
        private static bool CheckForColonInFirstPathSegment(String uriString)
        { 
            // Check for anything that may terminate the first regular path segment
            // or an illegal colon 
            char[] pathDelims = new char[] { ':', '\\', '/', '?', '#' }; 
            int index = uriString.IndexOfAny(pathDelims);
 
            return (index >= 0 && uriString[index] == ':');
        }

        // 
        // Checks if there are any unicode or escaped chars or ace to determine whether to load
        // config 
        // 
        private unsafe bool CheckForConfigLoad(String data)
        { 
            bool initConfig = false;
            int length = data.Length;

            fixed (char* temp = data){ 
                for (int i = 0; i < length; ++i){
 
                    if ((temp[i] > '\x7f') || (temp[i] == '%') || 
                        ((temp[i] == 'x') && ((i + 3) < length) && (temp[i + 1] == 'n') && (temp[i + 2] == '-') && (temp[i + 3] == '-')))
                    { 

                        // Unicode or maybe ace
                        initConfig = true;
                        break; 
                    }
                } 
 

            } 

            return initConfig;
        }
 
        //
        // Unescapes entire string and checks if it has unicode chars 
        // 
        private unsafe bool CheckForUnicode(String data)
        { 
            bool hasUnicode = false;
            char[] chars = new char[data.Length];
            int count = 0;
 
            chars = UnescapeString(data, 0, data.Length, chars, ref count, c_DummyChar,
                                   c_DummyChar, c_DummyChar, UnescapeMode.Unescape | UnescapeMode.UnescapeAll, 
                                   null, false, false); 

            String tempStr = new string(chars, 0, count); 
            int length = tempStr.Length;

            fixed (char* tempPtr = tempStr){
                for (int i = 0; i < length; ++i){ 
                    if (tempPtr[i] > '\x7f'){
                        // Unicode 
                        hasUnicode = true; 
                        break;
                    } 
                }
            }
            return hasUnicode;
 
        }
 
        // 
        // Check if the char (potentially surrogate) at given offset is in the iri range
        // Takes in isQuery because because iri restrictions for query are different 
        //
        internal static bool CheckIriUnicodeRange(string uri, int offset, ref bool surrogatePair, bool isQuery)
        {
            char invalidLowSurr = '\uFFFF'; 
            return CheckIriUnicodeRange(uri[offset],(offset + 1 < uri.Length) ? uri[offset + 1] : invalidLowSurr,
                                        ref surrogatePair, isQuery); 
        } 

        // 
        // Checks if provided non surrogate char lies in iri range
        //
        internal static bool CheckIriUnicodeRange(char unicode, bool isQuery)
        { 
            if ((unicode >= '\u00A0' && unicode <= '\uD7FF') ||
               (unicode >= '\uF900' && unicode <= '\uFDCF') || 
               (unicode >= '\uFDF0' && unicode <= '\uFFEF') || 
               (isQuery && unicode >= '\uE000' && unicode <= '\uF8FF')){
                return true; 
            }else{
                return false;
            }
        } 

        // 
        // Check if the highSurr is in the iri range or if highSurr and lowSurr are a surr pair then 
        // it checks if the combined char is in the range
        // Takes in isQuery because because iri restrictions for query are different 
        //
        internal static bool CheckIriUnicodeRange(char highSurr, char lowSurr, ref bool surrogatePair, bool isQuery)
        {
            bool inRange = false; 
            surrogatePair = false;
 
            if (CheckIriUnicodeRange(highSurr, isQuery)){ 
                inRange = true;
            } 
            else if (Char.IsHighSurrogate(highSurr)){
                if (Char.IsSurrogatePair(highSurr, lowSurr)){
                    surrogatePair = true;
                    char[] chars = new char[2] { highSurr, lowSurr }; 
                    string surrPair = new string(chars);
                    if (((surrPair.CompareTo("\U00010000") >= 0) && (surrPair.CompareTo("\U0001FFFD") <= 0)) || 
                        ((surrPair.CompareTo("\U00020000") >= 0) && (surrPair.CompareTo("\U0002FFFD") <= 0)) || 
                        ((surrPair.CompareTo("\U00030000") >= 0) && (surrPair.CompareTo("\U0003FFFD") <= 0)) ||
                        ((surrPair.CompareTo("\U00040000") >= 0) && (surrPair.CompareTo("\U0004FFFD") <= 0)) || 
                        ((surrPair.CompareTo("\U00050000") >= 0) && (surrPair.CompareTo("\U0005FFFD") <= 0)) ||
                        ((surrPair.CompareTo("\U00060000") >= 0) && (surrPair.CompareTo("\U0006FFFD") <= 0)) ||
                        ((surrPair.CompareTo("\U00070000") >= 0) && (surrPair.CompareTo("\U0007FFFD") <= 0)) ||
                        ((surrPair.CompareTo("\U00080000") >= 0) && (surrPair.CompareTo("\U0008FFFD") <= 0)) || 
                        ((surrPair.CompareTo("\U00090000") >= 0) && (surrPair.CompareTo("\U0009FFFD") <= 0)) ||
                        ((surrPair.CompareTo("\U000A0000") >= 0) && (surrPair.CompareTo("\U000AFFFD") <= 0)) || 
                        ((surrPair.CompareTo("\U000B0000") >= 0) && (surrPair.CompareTo("\U000BFFFD") <= 0)) || 
                        ((surrPair.CompareTo("\U000C0000") >= 0) && (surrPair.CompareTo("\U000CFFFD") <= 0)) ||
                        ((surrPair.CompareTo("\U000D0000") >= 0) && (surrPair.CompareTo("\U000DFFFD") <= 0)) || 
                        ((surrPair.CompareTo("\U000E0000") >= 0) && (surrPair.CompareTo("\U000EFFFD") <= 0)) ||
                        (isQuery && (((surrPair.CompareTo("\U000F0000") >= 0) && (surrPair.CompareTo("\U000FFFFD") <= 0)) ||
                        ((surrPair.CompareTo("\U00100000") >= 0) && (surrPair.CompareTo("\U0010FFFD") <= 0)))))
                        inRange = true; 
                }
            } 
 
            return inRange;
        } 
        //
        //
        //  Returns true if the string represents a valid argument to the Uri ctor
        //  If uriKind != AbsoluteUri then certain parsing erros are ignored but Uri usage is limited 
        //
        public static bool TryCreate(string uriString, UriKind uriKind, out Uri result) 
        { 
            if ((object)uriString == null)
            { 
                result = null;
                return false;
            }
            UriFormatException e = null; 
            result = CreateHelper(uriString, false, uriKind, ref e);
            return (object) e == null && result != null; 
        } 
        //
        public static bool TryCreate(Uri baseUri, string relativeUri, out Uri result) 
        {
            Uri relativeLink;
            if (TryCreate(relativeUri, UriKind.RelativeOrAbsolute, out relativeLink))
            { 
                if (!relativeLink.IsAbsoluteUri)
                    return TryCreate(baseUri, relativeLink, out result); 
 
                result = relativeLink;
                return true; 
            }
            result = null;
            return false;
        } 
        //
        public static bool TryCreate(Uri baseUri, Uri relativeUri, out Uri result) 
        { 
            result = null;
 
            //Consider: Work out the baseUri==null case
            if ((object)baseUri == null || (object)relativeUri == null)
                return false;
 
            if (baseUri.IsNotAbsoluteUri)
                return false; 
 
            UriFormatException e;
            string newUriString = null; 

            bool dontEscape;
            if (baseUri.Syntax.IsSimple)
            { 
                dontEscape = relativeUri.UserEscaped;
                result = ResolveHelper(baseUri, relativeUri, ref newUriString, ref dontEscape, out e); 
            } 
            else
            { 
                dontEscape = false;
                newUriString = baseUri.Syntax.InternalResolve(baseUri, relativeUri, out e);
            }
 
            if (e != null)
                return false; 
 
            if ((object) result == null)
                result = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e); 

            return (object) e == null && result != null && result.IsAbsoluteUri;
        }
        // 
        //
        public bool IsBaseOf(Uri uri) 
        { 
            if ((object)uri == null)
                throw new ArgumentNullException("uri"); 

            if (!IsAbsoluteUri)
                return false;
 
            if (Syntax.IsSimple)
                return IsBaseOfHelper(uri); 
 
            return Syntax.InternalIsBaseOf(this, uri);
        } 
        //
        //
        public string GetComponents(UriComponents components, UriFormat format)
        { 
            if (((components & UriComponents.SerializationInfoString) != 0) && components != UriComponents.SerializationInfoString)
                throw new ArgumentOutOfRangeException("UriComponents.SerializationInfoString"); 
 
            if ((format & ~UriFormat.SafeUnescaped) != 0)
                throw new ArgumentOutOfRangeException("format"); 

            if (IsNotAbsoluteUri)
            {
                if (components == UriComponents.SerializationInfoString) 
                    return GetRelativeSerializationString(format);
                else 
                    throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute)); 
            }
 
            if (Syntax.IsSimple)
                return GetComponentsHelper(components, format);

            return Syntax.InternalGetComponents(this, components, format); 
        }
        // 
        public bool IsWellFormedOriginalString() 
        {
            if (IsNotAbsoluteUri || Syntax.IsSimple) 
                return InternalIsWellFormedOriginalString();

            return Syntax.InternalIsWellFormedOriginalString(this);
        } 
        // Consider: (perf) Making it to not create a Uri internally
        public static bool IsWellFormedUriString(string uriString, UriKind uriKind) 
        { 
          Uri result;
 
          if (!Uri.TryCreate(uriString, uriKind, out result))
              return false;

          return result.IsWellFormedOriginalString(); 
        }
        // 
        // 
        // This is for languages that do not support == != operators overloading
        // 
        // Note that Uri.Equals will get an optimized path but is limited to true/fasle result only
        //
        public static int Compare(Uri uri1, Uri uri2, UriComponents partsToCompare, UriFormat compareFormat, StringComparison comparisonType)
        { 

            if ((object) uri1 == null) 
            { 
                if (uri2 == null)
                    return 0; // Equal 
                return -1;    // null < non-null
            }
            if ((object) uri2 == null)
                return 1;     // non-null > null 

            // a relative uri is always less than an absolute one 
            if (!uri1.IsAbsoluteUri || !uri2.IsAbsoluteUri) 
                return uri1.IsAbsoluteUri? 1: uri2.IsAbsoluteUri? -1: string.Compare(uri1.OriginalString, uri2.OriginalString, comparisonType);
 
            return string.Compare(
                                    uri1.GetParts(partsToCompare, compareFormat),
                                    uri2.GetParts(partsToCompare, compareFormat),
                                    comparisonType 
                                  );
        } 
        // 
        //
        // 
        public static string UnescapeDataString(string stringToUnescape)
        {
            if ((object) stringToUnescape == null)
                throw new ArgumentNullException("stringToUnescape"); 

            if (stringToUnescape.Length == 0) 
                return string.Empty; 

            unsafe { 
                fixed (char* pStr = stringToUnescape)
                {
                    int position;
                    for (position = 0; position < stringToUnescape.Length; ++position) 
                        if (pStr[position] == '%')
                            break; 
 
                    if (position == stringToUnescape.Length)
                        return stringToUnescape; 

                    position = 0;
                    char[] dest = new char[stringToUnescape.Length];
                    dest = UnescapeString(stringToUnescape, 0, stringToUnescape.Length, dest, ref position, c_DummyChar, c_DummyChar, c_DummyChar, 
                        UnescapeMode.Unescape | UnescapeMode.UnescapeAllOrThrow, null, false, true);
                    return new string(dest, 0, position); 
                } 
            }
        } 
        //
        // Where stringToEscape is intented to be a completely unescaped URI string.
        // This method will escape any character that is not a reserved or unreserved character, including percent signs.
        // Note that EscapeUriString will also do not escape a '#' sign. 
        //
        public static string EscapeUriString(string stringToEscape) 
        { 
            if ((object)stringToEscape == null)
                throw new ArgumentNullException("stringToUnescape"); 

            if (stringToEscape.Length == 0)
                return string.Empty;
 
            int position = 0;
            char[] dest = EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, true, c_DummyChar, c_DummyChar, c_DummyChar); 
            if ((object) dest == null) 
                return stringToEscape;
            return new string(dest, 0, position); 
        }
        //
        // Where stringToEscape is intended to be URI data, but not an entire URI.
        // This method will escape any character that is not an unreserved character, including percent signs. 
        //
        public static string EscapeDataString(string stringToEscape) 
        { 
            if ((object) stringToEscape == null)
                throw new ArgumentNullException("stringToUnescape"); 

            if (stringToEscape.Length == 0)
                return string.Empty;
 
            int position = 0;
            char[] dest = EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, false, c_DummyChar, c_DummyChar, c_DummyChar); 
            if (dest == null) 
                return stringToEscape;
            return new string(dest, 0, position); 
        }

        // - forceX characters are always escaped if found
        // - rsvd character will remain unescaped 
        //
        // start    - starting offset from input 
        // end      - the exclusive ending offset in input 
        // destPos  - starting offset in dest for output, on return this will be an exclusive "end" in the output.
        // 
        // In case "dest" has lack of space it will be reallocated by preserving the _whole_ content up to current destPos
        //
        // Returns null if nothing has to be escaped AND passed dest was null, otherwise the resulting array with the updated destPos
        // 
        const short c_MaxAsciiCharsReallocate   = 40;
        const short c_MaxUnicodeCharsReallocate = 40; 
        const short c_MaxUTF_8BytesPerUnicodeChar  = 4; 
        const short c_EncodedCharsPerByte = 3;
        private unsafe static char[] EscapeString(string input, int start, int end, char[] dest, ref int destPos, bool isUriString, char force1, char force2, char rsvd) 
        {
            if (end - start >= c_MaxUriBufferSize)
                throw GetException(ParsingError.SizeLimit);
 
            int i = start;
            int prevInputPos = start; 
            byte *bytes = stackalloc byte[c_MaxUnicodeCharsReallocate*c_MaxUTF_8BytesPerUnicodeChar];   // 40*4=160 

            fixed (char* pStr = input) 
            {
                for(; i < end; ++i)
                {
                    char ch = pStr[i]; 

                    // a Unicode ? 
                    if (ch  > '\x7F') 
                    {
                        short maxSize = (short)Math.Min(end - i, (int)c_MaxUnicodeCharsReallocate-1); 

                        short count = 1;
                        for (; count < maxSize && pStr[i + count] > '\x7f'; ++count)
                            ; 

                        // Is the last a high surrogate? 
                        if (pStr[i + count-1] >= 0xD800 && pStr[i + count-1] <= 0xDBFF) 
                        {
                            // Should be a rare case where the app tries to feed an invalid Unicode surrogates pair 
                           if (count == 1 || count == end - i)
                                throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
                           // need to grab one more char as a Surrogate except when it's a bogus input
                           ++count; 
                        }
 
                        dest = EnsureDestinationSize(pStr, dest, i, (short)(count*c_MaxUTF_8BytesPerUnicodeChar*c_EncodedCharsPerByte), 
                                                     c_MaxUnicodeCharsReallocate*c_MaxUTF_8BytesPerUnicodeChar*c_EncodedCharsPerByte,
                                                     ref destPos, prevInputPos); 

                        short numberOfBytes = (short)Encoding.UTF8.GetBytes(pStr+i, count, bytes, c_MaxUnicodeCharsReallocate*c_MaxUTF_8BytesPerUnicodeChar);

                        // This is the only exception that built in UriParser can throw after a Uri ctor. 
                        // Should not happen unless the app tries to feed an invalid Unicode String
                        if (numberOfBytes == 0) 
                            throw new UriFormatException(SR.GetString(SR.net_uri_BadString)); 

                        i += (count-1); 

                        for (count = 0 ; count < numberOfBytes; ++count)
                            EscapeAsciiChar((char)bytes[count], dest, ref destPos);
 
                        prevInputPos = i+1;
                    } 
                    else if (ch == '%' && rsvd == '%') 
                    {
                        // Means we don't reEncode '%' but check for the possible escaped sequence 
                        dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate*c_EncodedCharsPerByte, ref destPos, prevInputPos);
                        if(i + 2 < end && EscapedAscii(pStr[i+1], pStr[i+2]) != c_DummyChar)
                        {
                            // leave it escaped 
                            dest[destPos++] = '%';
                            dest[destPos++] = pStr[i+1]; 
                            dest[destPos++] = pStr[i+2]; 
                            i += 2;
                        } 
                        else
                        {
                            EscapeAsciiChar('%', dest, ref destPos);
                        } 
                        prevInputPos = i+1;
                    } 
                    else if (ch == force1 ||  ch == force2) 
                    {
                        dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate*c_EncodedCharsPerByte, ref destPos, prevInputPos); 
                        EscapeAsciiChar(ch, dest, ref destPos);
                        prevInputPos = i+1;
                    }
                    else if (ch != rsvd && (isUriString? IsNotReservedNotUnreservedNotHash(ch): IsNotUnreserved(ch))) 
                    {
                        dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate*c_EncodedCharsPerByte, ref destPos, prevInputPos); 
                        EscapeAsciiChar(ch, dest, ref destPos); 
                        prevInputPos = i+1;
                    } 
                }

                if (prevInputPos != i)
                { 
                    // need to fill up the dest array ?
                    if (prevInputPos != start || dest != null) 
                        dest = EnsureDestinationSize(pStr, dest, i, 0, 0, ref destPos, prevInputPos); 
                }
            } 

            return dest;
        }
 
        //
        // ensure destination array has enough space and contains all the needed input stuff 
        // 
        private unsafe static char[] EnsureDestinationSize(char *pStr, char[] dest, int currentInputPos, short charsToAdd, short minReallocateChars, ref int destPos, int prevInputPos)
        { 
            if ((object) dest == null || dest.Length  < destPos + (currentInputPos-prevInputPos) + charsToAdd)
            {
                // allocating or reallocating array by ensuring enough space based on maxCharsToAdd.
                char[] newresult = new char[destPos + (currentInputPos-prevInputPos) + minReallocateChars]; 

                if ((object) dest != null && destPos != 0) 
                    Buffer.BlockCopy(dest, 0, newresult, 0, destPos<<1); 
                dest = newresult;
            } 

            // ensuring we copied everything form the input string left before last escaping
            while (prevInputPos != currentInputPos)
                dest[destPos++] = pStr[prevInputPos++]; 
            return dest;
        } 
        // 
        // mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
        // reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," 
        // excluded = control | space | delims | unwise
        // delims      = "<" | ">" | "#" | "%" | <">
        // unwise      = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"
        // 
        private static unsafe bool IsNotReservedNotUnreservedNotHash(char c)
        { 
            if (c > 'z' && c != '~') 
            {
                return true; 
            }
            else if (c > 'Z' && c < 'a' && c != '_')
            {
                return true; 
            }
            else if (c < '!') 
            { 
                return true;
            } 
            else if (c == '>' || c == '<' || c == '%' || c == '"' || c == '`')
            {
                return true;
            } 
            return false;
        } 
        // 
        private static unsafe bool IsNotUnreserved(char c)
        { 
            if (c > 'z' && c != '~')
            {
                return true;
            } 
            else if ((c > '9' && c < 'A') || (c > 'Z' && c < 'a' && c != '_'))
            { 
                return true; 
            }
            else if (c < '\'' && c != '!') 
            {
                return true;
            }
            else if (c == '+' || c == ',' || c == '/') 
            {
                return true; 
            } 
            return false;
        } 

        //
        // This method will assume that any good Escaped Sequence will be unescaped in the output
        // - Assumes Dest.Length - detPosition >= end-start 
        // - UnescapeLevel controls various modes of opearion
        // - Any "bad" escape sequence will remain as is or '%' will be escaped. 
        // - destPosition tells the starting index in dest for placing the result. 
        //   On return destPosition tells the last character + 1 postion in the "dest" array.
        // - The control chars and chars passed in rsdvX parameters may be re-escaped depending on UnescapeLevel 
        // - It is a RARE case when Unescape actually needs escaping some characteres mentioned above.
        //   For this reason it returns a char[] that is usually the same ref as the input "dest" value.
        //
        [Flags] 
        private enum UnescapeMode
        { 
            CopyOnly                = 0x0,                  // used for V1.0 ToString() compatibility mode only 
            Escape                  = 0x1,                  // Only used by ImplicitFile, the string is already fully unescaped
            Unescape                = 0x2,                  // Only used as V1.0 UserEscaped compatibility mode 
            EscapeUnescape          = Unescape|Escape,      // does both escaping control+reserved and unescaping of safe characters
            V1ToStringFlag          = 0x4,                  // Only used as V1.0 ToString() compatibility mode, assumes DontEscape level also
            UnescapeAll             = 0x8,                  // just unescape everything, leave bad escaped sequences as is (should throw but can't due to v1.0 compat)
            UnescapeAllOrThrow      = 0x10|UnescapeAll,     // just unescape everything plus throw on bad escaped sequences 
        }
        private unsafe static char[] UnescapeString(   string input, int start, int end, char[] dest, ref int destPosition, 
                                                        char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, 
                                                        UriParser syntax, bool isQuery, bool readOnlyConfig)
        { 
            fixed (char *pStr = input)
            {
                return UnescapeString(  pStr, start, end, dest, ref destPosition,
                                        rsvd1, rsvd2, rsvd3, unescapeMode, syntax, isQuery, readOnlyConfig); 
            }
        } 
        private unsafe static char[] UnescapeString(char *pStr, int start, int end, char[] dest, ref int destPosition, 
            char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser syntax, bool isQuery, bool readOnlyConfig)
        { 
            byte [] bytes = null;
            byte escapedReallocations = 0;
            bool escapeReserved = false;
            int next = start; 
            bool iriParsing = s_IriParsing && ((readOnlyConfig) || (!readOnlyConfig && IriParsingStatic(syntax)))
                                && ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.EscapeUnescape); 
 
            while (true)
            { 
                // we may need to re-pin dest[]
                fixed (char* pDest = dest)
                {
                    if ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.CopyOnly) 
                    {
                        while (start < end) 
                            pDest[destPosition++] = pStr[start++]; 
                        return dest;
                    } 

                    while (true)
                    {
                        char ch = (char)0; 

                        for (;next < end; ++next) 
                        { 
                            if ((ch = pStr[next]) == '%')
                            { 
                                if ((unescapeMode & UnescapeMode.Unescape) == 0)
                                {
                                    // re-escape, don't check anything else
                                    escapeReserved = true; 
                                }
                                else if (next+2 < end) 
                                { 
                                    ch = EscapedAscii(pStr[next+1], pStr[next+2]);
                                    // Unescape a good sequence if full unescape is requested 
                                    if (unescapeMode >= UnescapeMode.UnescapeAll)
                                    {
                                        if (ch == c_DummyChar)
                                        { 
                                            if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow)
                                            { 
                                                // Should be a rare case where the app tries to feed an invalid escaped sequence 
                                                throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
                                            } 
                                            continue;
                                        }
                                    }
                                    // re-escape % from an invalid sequence 
                                    else if (ch == c_DummyChar)
                                    { 
                                        if ((unescapeMode & UnescapeMode.Escape) != 0) 
                                            escapeReserved = true;
                                        else 
                                            continue;   // we should throw instead but since v1.0 woudl just print '%'
                                    }
                                    // Do not unescape '%' itself unless full unescape is requested
                                    else if (ch == '%') 
                                    {
                                        next += 2; 
                                        continue; 
                                    }
                                    // Do not unescape a reserved char unless full unescape is requested 
                                    else if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3)
                                    {
                                        next += 2;
                                        continue; 
                                    }
                                    // Do not unescape a dangerous char unless it's V1ToStringFlags mode 
                                    else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && IsNotSafeForUnescape(ch)) 
                                    {
                                        next += 2; 
                                        continue;
                                    }
                                    else if (iriParsing && ((ch <='\x9F' && IsNotSafeForUnescape(ch)) ||
                                                            (ch >'\x9F' &&!CheckIriUnicodeRange(ch, isQuery)))) 
                                    {
                                        // check if unenscaping gives a char ouside iri range 
                                        // if it does then keep it escaped 
                                        next += 2;
                                        continue; 
                                    }
                                    // unescape escaped char or escape %
                                    break;
                                } 
                                else if (unescapeMode >= UnescapeMode.UnescapeAll)
                                { 
                                    if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) 
                                    {
                                        // Should be a rare case where the app tries to feed an invalid escaped sequence 
                                        throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
                                    }
                                    // keep a '%' as part of a bogus sequence (we should throw but this is how 1.0 has shipped)
                                    continue; 
                                }
                                else 
                                { 
                                    escapeReserved = true;
                                } 
                                // escape (escapeReserved==ture) or otheriwse unescape the sequence
                                break;
                            }
                            else if ((unescapeMode & (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) == (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) 
                            {
                                continue; 
                            } 
                            else if ((unescapeMode & UnescapeMode.Escape) != 0)
                            { 
                                 // Could actually escape some of the characters
                                 if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3)
                                 {
                                     // found an unescaped reserved character -> escape it 
                                     escapeReserved = true;
                                     break; 
                                 } 
                                 else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && (ch <= '\x1F' || (ch >= '\x7F' && ch <= '\x9F')))
                                 { 
                                     // found an unescaped reserved character -> escape it
                                     escapeReserved = true;
                                     break;
                                 } 
                            }
                        } 
 
                        //copy off previous characters from input
                        while (start < next) 
                            pDest[destPosition++] = pStr[start++];

                        if (next != end)
                        { 
                            //VsWhidbey#87423
                            if (escapeReserved) 
                            { 
                                //escape that char
                                // Since this should be _really_ rare case, reallocate with constant size increase of 30 rsvd-type characters. 
                                if (escapedReallocations == 0)
                                {
                                    escapedReallocations = 30;
                                    char[] newDest = new char[dest.Length + escapedReallocations*3]; 
                                    fixed (char *pNewDest = newDest)
                                    { 
                                        for (int i = 0; i < destPosition; ++i) 
                                            pNewDest[i] = pDest[i];
                                    } 
                                    dest = newDest;
                                    // re-pin new dest[] array
                                    goto dest_fixed_loop_break;
                                } 
                                else
                                { 
                                    --escapedReallocations; 
                                    EscapeAsciiChar(pStr[next], dest, ref destPosition);
                                    escapeReserved = false; 
                                    start = ++next;
                                    continue;
                                }
                            } 

                            // unescaping either one Ascii or possibly multiple Unicode 
 
                            if (ch <= '\x7F')
                            { 
                                //ASCII
                                dest[destPosition++] = ch;
                                next+=3;
                                start = next; 
                                continue;
                            } 
 
                            // Unicode
 
                            int byteCount = 1;
                            // lazy initialization of max size, will reuse the array for next sequences
                            if ((object) bytes == null)
                                bytes = new byte[end - next]; 

                            bytes[0] = (byte)ch; 
                            next+=3; 
                            while (next < end)
                            { 
                                // Check on exit criterion
                                if ((ch = pStr[next]) != '%' || next+2 >= end)
                                    break;
 
                                // already made sure we have 3 characters in str
                                ch = EscapedAscii(pStr[next+1], pStr[next+2]); 
 
                                //invalid hex sequence ?
                                if (ch == c_DummyChar) 
                                    break;
                                // character is not part of a UTF-8 sequence ?
                                else if (ch < '\x80')
                                    break; 
                                else
                                { 
                                    //a UTF-8 sequence 
                                    bytes[byteCount++] = (byte)ch;
                                    next += 3; 
                                }
                            }
                            Encoding noFallbackCharUTF8 = Encoding.GetEncoding("utf-8", new EncoderReplacementFallback(""), new DecoderReplacementFallback(""));
                            char[] unescapedChars = new char[bytes.Length]; 
                            int charCount = noFallbackCharUTF8.GetChars(bytes, 0, byteCount, unescapedChars, 0);
 
                            if (charCount != 0) 
                            {
                                start = next; 

                                // match exact bytes
                                // Do not unescape chars not allowed by Iri
                                // need to check for invalid utf sequences that may not have given any chars 

                                MatchUTF8Sequence(pDest, dest, ref destPosition, unescapedChars, charCount, bytes, isQuery, iriParsing); 
                            } 
                            else
                            { 
                                // the encoded, high-ANSI characters are not UTF-8 encoded
                                // [....] codereview: a bug?
                                // If we expect UTF-8 then we should discard (not copy) anything else.
                                // The reason is that >0x7F Ansi and UTF-8 cannot be supported at the same time 

                                if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) 
                                { 
                                    // Should be a rare case where the app tries to feed an invalid escaped sequence
                                    throw new UriFormatException(SR.GetString(SR.net_uri_BadString)); 
                                }

                                next = start + 3;
                                start = next; 
                                dest[destPosition++]= (char)bytes[0];
                            } 
                        } 

                        if (next == end) 
                            goto done;
                    }
dest_fixed_loop_break: ;
                } 
            }
 
done:       return dest; 
        }
 
        //
        // Need to check for invalid utf sequences that may not have given any chars.
        // We got the unescaped chars, we then reencode them and match off the bytes
        // to get the invalid sequence bytes that we just copy off 
        //
        private static unsafe void MatchUTF8Sequence(char* pDest, char[] dest, ref int destOffset, char[] unescapedChars, 
                                                    int charCount, byte[] bytes, bool isQuery, bool iriParsing) 
        {
            int count = 0; 
            fixed (char* unescapedCharsPtr = unescapedChars){
                for (int j = 0; j < charCount; ++j){
                    bool isHighSurr = Char.IsHighSurrogate(unescapedCharsPtr[j]);
                    byte[] encodedBytes = Encoding.UTF8.GetBytes(unescapedChars, j, isHighSurr ? 2 : 1); 
                    int encodedBytesLength = encodedBytes.Length;
 
                    // we have to keep unicode chars outside Iri range escaped 
                    bool inIriRange = false;
                    if (iriParsing){ 
                        if (!isHighSurr)
                            inIriRange = CheckIriUnicodeRange(unescapedChars[j], isQuery);
                        else{
                            bool surrPair = false; 
                            inIriRange = CheckIriUnicodeRange(unescapedChars[j], unescapedChars[j + 1],
                                                                   ref surrPair, isQuery); 
                        } 
                    }
 
                    while (true){
                        while (bytes[count] != encodedBytes[0]){
                            EscapeAsciiChar((char)bytes[count++], dest, ref destOffset);
                        } 

                        // check if all bytes match 
                        bool allBytesMatch = true; 
                        int k = 0;
                        for (; k < encodedBytesLength; ++k){ 
                            if (bytes[count + k] != encodedBytes[k]){
                                allBytesMatch = false;
                                break;
                            } 
                        }
 
                        if (allBytesMatch){ 
                            count += encodedBytesLength;
                            if (iriParsing){ 
                                if (!inIriRange){
                                    // need to keep chars not allowed as escaped
                                    for (int l = 0; l < encodedBytes.Length; ++l){
                                        EscapeAsciiChar((char)encodedBytes[l], dest, ref destOffset); 
                                    }
                                } 
                                else if (!IsBidiControlCharacter(unescapedCharsPtr[j])){ 
                                    //copy chars
                                    pDest[destOffset++] = unescapedCharsPtr[j]; 
                                }
                                if (isHighSurr)
                                    pDest[destOffset++] = unescapedCharsPtr[j + 1];
                            } 
                            else{
                                //copy chars 
                                pDest[destOffset++] = unescapedCharsPtr[j]; 

                                if (isHighSurr) 
                                    pDest[destOffset++] = unescapedCharsPtr[j + 1];
                            }
                                break; // break out of while (true) since we've matched this char bytes
                        } 
                        else{
                            // copy bytes till place where bytes dont match 
                            for (int l = 0; l < k; ++l){ 
                                EscapeAsciiChar((char)bytes[count++], dest, ref destOffset);
                            } 
                        }
                    }

                    if (isHighSurr) j++; 

                } 
            } 
        }
 
        // for rfc 3987
        internal bool CheckIsReserved(char ch)
        {
            char[] rsvd = new char[]{':', '/', '?', '#', '[', ']', '@'}; //gen-delims 
            for (int i = 0; i < rsvd.Length; ++i){
                if (rsvd[i] == ch) return true; 
            } 
            return false;
        } 

        //
        // Check reserved chars according to rfc 3987 in a sepecific component
        // 
        internal bool CheckIsReserved(char ch, UriComponents component)
        { 
            if ((component != UriComponents.Scheme) || 
                    (component != UriComponents.UserInfo) ||
                    (component != UriComponents.Host) || 
                    (component != UriComponents.Port) ||
                    (component != UriComponents.Path) ||
                    (component != UriComponents.Query) ||
                    (component != UriComponents.Fragment) 
                )
                return (component == (UriComponents)0)? CheckIsReserved(ch): false; 
            else 
            {
                switch(component) 
                {
                    // Reserved chars according to rfc 3987
                    case UriComponents.UserInfo:
                        if( ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' || ch == '@' ) 
                            return true;
                        break; 
                    case UriComponents.Host: 
                        if( ch == ':' || ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' || ch == '@' )
                            return true; 
                        break;
                    case UriComponents.Path:
                        if( ch == '/' || ch == '?' || ch == '#' || ch == '[' || ch == ']' )
                            return true; 
                        break;
                    case UriComponents.Query: 
                        if(ch == '#' || ch == '[' || ch == ']') 
                            return true;
                        break; 
                    case UriComponents.Fragment:
                        if(ch == '#' || ch == '[' || ch == ']')
                            return true;
                        break; 
                    default:
                        break; 
                } 
                return false;
            } 
        }

        //
        // Cleans up the specified component according to Iri rules 
        // a) Chars allowed by iri in a component are unescaped if found escaped
        // b) Bidi chars are stripped 
        // 
        // should be called only if IRI parsing is switched on
        internal unsafe string EscapeUnescapeIri(string input, int start, int end, UriComponents component) 
        {
            fixed (char *pInput = input)
            {
                return EscapeUnescapeIri(pInput, start, end, component); 
            }
        } 
 
        //
        // See above explanation 
        //
        internal unsafe string EscapeUnescapeIri(char* pInput, int start, int end, UriComponents component)
        {
 
            char [] dest = new char[ end - start ];
            byte[] bytes = null; 
 
            // Pin the array to do pointer accesses
            GCHandle destHandle = GCHandle.Alloc(dest, GCHandleType.Pinned); 
            char* pDest = (char*)destHandle.AddrOfPinnedObject();

            byte escapedReallocations = 0;
            int next = start; 
            int destOffset = 0;
            char ch; 
            bool escape = false; 
            bool surrogatePair = false;
            bool isUnicode = false; 

            for (;next < end; ++next)
            {
                escape = false; 
                surrogatePair = false;
                isUnicode = false; 
 
                if ((ch = pInput[next]) == '%'){
                    if (next + 2 < end){ 
                        ch = EscapedAscii(pInput[next + 1], pInput[next + 2]);
                        // Do not unescape a reserved char
                        if (ch == c_DummyChar || ch == '%' || CheckIsReserved(ch, component) || IsNotSafeForUnescape(ch)){
                            // keep as is 
                            pDest[destOffset++] = pInput[next++];
                            pDest[destOffset++] = pInput[next++]; 
                            pDest[destOffset++] = pInput[next]; 
                            continue;
                        } 
                        else if (ch <= '\x7F'){
                            //ASCII
                            pDest[destOffset++] = ch;
                            next += 2; 
                            continue;
                        }else{ 
                            // possibly utf8 encoded sequence of unicode 

                            // check if safe to unescape according to Iri rules 

                            int startSeq = next;
                            int byteCount = 1;
                            // lazy initialization of max size, will reuse the array for next sequences 
                            if ((object)bytes == null)
                                bytes = new byte[end - next]; 
 
                            bytes[0] = (byte)ch;
                            next += 3; 
                            while (next < end)
                            {
                                // Check on exit criterion
                                if ((ch = pInput[next]) != '%' || next + 2 >= end) 
                                    break;
 
                                // already made sure we have 3 characters in str 
                                ch = EscapedAscii(pInput[next + 1], pInput[next + 2]);
 
                                //invalid hex sequence ?
                                if (ch == c_DummyChar)
                                    break;
                                // character is not part of a UTF-8 sequence ? 
                                else if (ch < '\x80')
                                    break; 
                                else 
                                {
                                    //a UTF-8 sequence 
                                    bytes[byteCount++] = (byte)ch;
                                    next += 3;
                                }
                            } 
                            next--; // for loop will increment
 
                            Encoding noFallbackCharUTF8 = Encoding.GetEncoding("utf-8",  new EncoderReplacementFallback(""), new DecoderReplacementFallback("")); 
                            char[] unescapedChars = new char[bytes.Length];
                            int charCount = noFallbackCharUTF8.GetChars(bytes, 0, byteCount, unescapedChars, 0); 


                            if (charCount != 0){
 
                                // need to check for invalid utf sequences that may not have given any chars
 
                                // check if unicode value is allowed 
                                MatchUTF8Sequence(  pDest, dest, ref destOffset, unescapedChars, charCount, bytes,
                                                    component == UriComponents.Query, true); 
                            }
                            else
                            {
                                // copy escaped sequence as is 
                                for (int i = startSeq; i <= next; ++i)
                                    pDest[destOffset++] = pInput[i]; 
                            } 

                        } 

                    }else{
                        pDest[destOffset++] = pInput[next];
                    } 
                }
                else if (ch > '\x7f'){ 
                    // unicode 

                    char ch2; 

                    if ((Char.IsHighSurrogate(ch)) && (next + 1 < end)){
                        ch2 = pInput[next + 1];
                        escape = !CheckIriUnicodeRange(ch, ch2, ref surrogatePair, component == UriComponents.Query); 
                        if (!escape){
                            // copy the two chars 
                            pDest[destOffset++] = pInput[next++]; 
                            pDest[destOffset++] = pInput[next];
                        }else{ 
                            isUnicode = true;
                        }
                    }else{
                        if(CheckIriUnicodeRange(ch, component == UriComponents.Query)){ 
                            if (!IsBidiControlCharacter(ch)){
                                // copy it 
                                pDest[destOffset++] = pInput[next]; 
                            }
                        }else{ 
                            // escape it
                            escape = true;
                            isUnicode = true;
                        } 
                    }
                }else{ 
                    // just copy the character 
                    pDest[destOffset++] = pInput[next];
                } 

                if (escape){
                    if (escapedReallocations == 0){
                        // may need more memory since we didnt anticipate escaping 
                        escapedReallocations = 30;
 
                        char[] newDest = new char[dest.Length + escapedReallocations * 3]; 
                        fixed (char* pNewDest = newDest){
                            for (int i = 0; i < destOffset; ++i) 
                                pNewDest[i] = pDest[i];
                        }
                        if (destHandle.IsAllocated)
                            destHandle.Free(); 
                        dest = newDest;
 
                        // re-pin new dest[] array 
                        destHandle = GCHandle.Alloc(dest, GCHandleType.Pinned);
                        pDest = (char*)destHandle.AddrOfPinnedObject(); 
                    }else{
                        if (isUnicode){
                            if (surrogatePair)
                                escapedReallocations -= 4; 
                            else
                                escapedReallocations -= 3; 
                        } 
                        else
                            --escapedReallocations; 
                    }

                    byte[] encodedBytes = new byte[4];
                    fixed (byte* pEncodedBytes = encodedBytes){ 
                        int encodedBytesCount = Encoding.UTF8.GetBytes(pInput + next, surrogatePair ? 2 : 1, pEncodedBytes, 4);
 
                        for (int count = 0; count < encodedBytesCount; ++count) 
                            EscapeAsciiChar((char)encodedBytes[count], dest, ref destOffset);
                    } 
                }
            }

            if (destHandle.IsAllocated) 
                destHandle.Free();
            return new string(dest, 0 , destOffset ); 
        } 

        // 
        // Do not unescape these in safe mode:
        // 1)  reserved    = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
        // 2)  excluded = control | "#" | "%" | "\"
        // 
        // That will still give plenty characters unescaped by SafeUnesced mode such as
        // 1) Unicode characters 
        // 2) Unreserved = alphanum | "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" 
        // 3) DelimitersAndUnwise = "<" | ">" |  <"> | "{" | "}" | "|" | "^" | "[" | "]" | "`"
        static bool IsNotSafeForUnescape(char ch) 
        {
            if (ch <= '\x1F' || (ch >= '\x7F' && ch <= '\x9F'))
                return true;
            else if ((ch >= ';' && ch <= '@' && (ch | '\x2') != '>') || 
                     (ch >= '#' && ch <= '&') ||
                     ch == '+' || ch == ',' || ch == '/' || ch == '\\') 
                return true; 

            return false; 
        }


        // 
        // Internal stuff
        // 
 
        // Returns false if OriginalString value
        // (1) is not correctly escaped as per URI spec excluding intl UNC name case 
        // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file"
        // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file"
        // (4) or contains unescaped backslashes even if they will be treated
        //     as forward slashes like http:\\host/path\file or file:\\\c:\path 
        //
        internal unsafe bool InternalIsWellFormedOriginalString() 
        { 
            if (UserDrivenParsing)
                throw new InvalidOperationException(SR.GetString(SR.net_uri_UserDrivenParsing, this.GetType().FullName)); 

            fixed (char* str = m_String)
            {
                ushort idx = 0; 
                //
                // For a relative Uri we only care about escaping and backslashes 
                // 
                if (!IsAbsoluteUri)
                    return (CheckCanonical(str, ref idx, (ushort)m_String.Length, c_EOL) & (Check.BackslashInPath | Check.EscapedCanonical)) == Check.EscapedCanonical; 

                //
                // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file"
                // 
                if (IsImplicitFile)
                    return false; 
 
                //This will get all the offsets, a Host name will be checked separatelly below
                EnsureParseRemaining(); 

                Flags nonCanonical = (m_Flags & (Flags.E_CannotDisplayCanonical | Flags.IriCanonical));
                // User, Path, Query or Fragment may have some non escaped characters
                if (((nonCanonical & Flags.E_CannotDisplayCanonical & (Flags.E_UserNotCanonical | Flags.E_PathNotCanonical | 
                                        Flags.E_QueryNotCanonical | Flags.E_FragmentNotCanonical)) != Flags.Zero) &&
                    (!m_iriParsing || (m_iriParsing && 
                    (((nonCanonical & Flags.E_UserNotCanonical) == 0) || ((nonCanonical & Flags.UserIriCanonical) == 0)) && 
                    (((nonCanonical & Flags.E_PathNotCanonical) == 0) || ((nonCanonical & Flags.PathIriCanonical) == 0)) &&
                    (((nonCanonical & Flags.E_QueryNotCanonical) == 0) || ((nonCanonical & Flags.QueryIriCanonical) == 0)) && 
                    (((nonCanonical & Flags.E_FragmentNotCanonical) == 0) || ((nonCanonical & Flags.FragmentIriCanonical) == 0)))))
                    return false;

                // checking on scheme:\\ or file://// 
                if (InFact(Flags.AuthorityFound))
                { 
                    idx = (ushort)(m_Info.Offset.Scheme + m_Syntax.SchemeName.Length + 2); 
                    if (idx >= m_Info.Offset.User || m_String[idx - 1] == '\\' || m_String[idx] == '\\')
                        return false; 

#if !PLATFORM_UNIX
                    if (InFact(Flags.UncPath | Flags.DosPath))
                    { 
                        while (++idx < m_Info.Offset.User && (m_String[idx] == '/' || m_String[idx] == '\\'))
                            return false; 
                    } 
#endif // !PLATFORM_UNIX
                } 


                // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file"
                // Note that for this check to be more general we assert that if Path is non empty and if it requires a first slash 
                // (which looks absent) then the method has to fail.
                // Today it's only possible for a Dos like path, i.e. file://c:/bla would fail below check. 
                if (InFact(Flags.FirstSlashAbsent) && m_Info.Offset.Query > m_Info.Offset.Path) 
                    return false;
 
                // (4) or contains unescaped backslashes even if they will be treated
                //     as forward slashes like http:\\host/path\file or file:\\\c:\path
                // Note we do not check for Flags.ShouldBeCompressed i.e. allow // /./ and alike as valid
                if (InFact(Flags.BackslashInPath)) 
                    return false;
 
                // Capturing a rare case like file:///c|/dir 
                if (IsDosPath && m_String[m_Info.Offset.Path + SecuredPathIndex - 1] == '|')
                    return false; 

                //
                // May need some real CPU processing to anwser the request
                // 
                //
                // Check escaping for authority 
                // 
                if ((m_Flags & Flags.CanonicalDnsHost) == 0)
                { 
                    idx = m_Info.Offset.User;
                    if (!(m_iriParsing && (HostType == Flags.IPv6HostType))){
                        Check result = CheckCanonical(str, ref idx, (ushort)m_Info.Offset.Path, '/');
                        if (((result & (Check.ReservedFound | Check.BackslashInPath | Check.EscapedCanonical)) != Check.EscapedCanonical) && 
                            (!m_iriParsing || (m_iriParsing && ((result & (Check.DisplayCanonical | Check.FoundNonAscii | Check.NotIriCanonical))
                                                            != (Check.DisplayCanonical | Check.FoundNonAscii))))) 
                            return false; 
                    }
                } 

                // Want to ensure there are slashes after the scheme
                if ((m_Flags & (Flags.SchemeNotCanonical | Flags.AuthorityFound)) == (Flags.SchemeNotCanonical | Flags.AuthorityFound))
                { 
                    idx = (ushort)m_Syntax.SchemeName.Length;
                    while (str[idx++] != ':') ; 
                    if (idx + 1 >= m_String.Length || str[idx] != '/' || str[idx + 1] != '/') 
                        return false;
                } 
            }
            //
            // May be scheme, host, port or path need some canonicalization but still the uri string is found to be a "well formed" one
            // 
            return true;
        } 
 

        // Should never be used except by the below method 
        private Uri(Flags flags, UriParser uriParser, string uri)
        {
            m_Flags = flags;
            m_Syntax = uriParser; 
            m_String = uri;
        } 
        // 
        // a Uri.TryCreate() method goes through here.
        // 
        internal static Uri CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException e)
        {
            // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow to be used here.
            if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative){ 
                throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind));
            } 
 
            UriParser syntax = null;
            Flags flags = Flags.Zero; 
            ParsingError err = ParseScheme(uriString, ref flags, ref syntax);

            if (dontEscape)
                flags |= Flags.UserEscaped; 

            // We won't use User factory for these errors 
            if (err != ParsingError.None) 
            {
                // If it looks as a relative Uri, custom factory is ignored 
                if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex)
                    return new Uri((flags & Flags.UserEscaped), null, uriString);

                return null; 
            }
 
            // Cannot be relative Uri if came here 
            Uri result = new Uri(flags, syntax, uriString);
 
            // Validate instance using ether built in or a user Parser
            try
            {
                result.InitializeUri(err, uriKind, out e); 

                if (e == null) 
                    return result; 

                return null; 
            }
            catch (UriFormatException ee)
            {
                System.Net.GlobalLog.Assert(!syntax.IsSimple, "A UriPraser threw on InitializeAndValidate."); 
                e = ee;
                // A precaution since custom Parser should never throw in this case. 
                return null; 
            }
        } 
        //
        // Resolves into either baseUri or relativeUri according to conditions OR if not possible it uses newUriString
        // to  return combined URI strings from both Uris
        // otherwise if e != null on output the operation has failed 
        //
 
        internal static Uri ResolveHelper(Uri baseUri, Uri relativeUri, ref string newUriString, ref bool userEscaped, out UriFormatException e) 
        {
            System.Net.GlobalLog.Assert(!baseUri.IsNotAbsoluteUri && !baseUri.UserDrivenParsing, "Uri::ResolveHelper()|baseUri is not Absolute or is controlled by User Parser."); 

            e = null;
            string relativeStr = string.Empty;
 
            if ((object)relativeUri != null)
            { 
                if (relativeUri.IsAbsoluteUri) 
                    return relativeUri;
 
                relativeStr = relativeUri.OriginalString;
                userEscaped = relativeUri.UserEscaped;
            }
            else 
                relativeStr = string.Empty;
 
            // Here we can assert that passed "relativeUri" is indeed a relative one 

            if (relativeStr.Length > 0 && (IsLWS(relativeStr[0]) || IsLWS(relativeStr[relativeStr.Length - 1]))) 
                relativeStr = relativeStr.Trim(_WSchars);

            if (relativeStr.Length == 0)
            { 
                newUriString = baseUri.GetParts(UriComponents.AbsoluteUri, baseUri.UserEscaped ? UriFormat.UriEscaped : UriFormat.SafeUnescaped);
                return null; 
            } 

            // Check for a simple fragment in relative part 
            if (relativeStr[0] == '#' && !baseUri.IsImplicitFile && baseUri.Syntax.InFact(UriSyntaxFlags.MayHaveFragment))
            {
                newUriString = baseUri.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.UriEscaped) + relativeStr;
                return null; 
            }
 
            // Check for a simple query in relative part 
            if (relativeStr[0] == '?' && !baseUri.IsImplicitFile && baseUri.Syntax.InFact(UriSyntaxFlags.MayHaveQuery))
            { 
                newUriString = baseUri.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Query & ~UriComponents.Fragment,
                    UriFormat.UriEscaped) + relativeStr;
                return null;
            } 

            // Check on the DOS path in the relative Uri (a special case) 
            if (relativeStr.Length >= 3 
                && (relativeStr[1] == ':' || relativeStr[1] == '|')
                && IsAsciiLetter(relativeStr[0]) 
                && (relativeStr[2] == '\\' || relativeStr[2] == '/'))
            {

                if (baseUri.IsImplicitFile) 
                {
                    // It could have file:/// prepended to the result but we want to keep it as *Implicit* File Uri 
                    newUriString = relativeStr; 
                    return null;
                } 
                else if (baseUri.Syntax.InFact(UriSyntaxFlags.AllowDOSPath))
                {
                    // The scheme is not changed just the path gets replaced
                    string prefix; 
                    if (baseUri.InFact(Flags.AuthorityFound))
                        prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":///" : "://"; 
                    else 
                        prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":/" : ":";
 
                    newUriString = baseUri.Scheme + prefix + relativeStr;
                    return null;
                }
                // If we are here then input like "http://host/path/" + "C:\x" will produce the result  http://host/path/c:/x 
            }
 
 
            ParsingError err = GetCombinedString(baseUri, relativeStr, userEscaped, ref newUriString);
 
            if (err != ParsingError.None)
            {
                e = GetException(err);
                return null; 
            }
 
            if ((object)newUriString == (object)baseUri.m_String) 
                return baseUri;
 
            return null;
        }

        private unsafe string GetRelativeSerializationString(UriFormat format) 
        {
            if (format == UriFormat.UriEscaped) 
            { 
                if (m_String.Length == 0)
                    return string.Empty; 
                int position = 0;
                char[] dest = EscapeString(m_String, 0, m_String.Length, null, ref position, true, c_DummyChar, c_DummyChar, '%');
                if ((object)dest == null)
                    return m_String; 
                return new string(dest, 0, position);
            } 
 
            else if (format == UriFormat.Unescaped)
                return UnescapeDataString(m_String); 

            else if (format == UriFormat.SafeUnescaped)
            {
                if (m_String.Length == 0) 
                    return string.Empty;
 
                char[] dest = new char[m_String.Length]; 
                int position = 0;
                dest = UnescapeString(m_String, 0, m_String.Length, dest, ref position, c_DummyChar, c_DummyChar, c_DummyChar, 
                    UnescapeMode.EscapeUnescape, null, false, true);
                return new string(dest, 0, position);
            }
            else 
                throw new ArgumentOutOfRangeException("format");
 
        } 

        // 
        // UriParser helpers methods
        //
        internal string GetComponentsHelper(UriComponents uriComponents, UriFormat uriFormat)
        { 
            if (uriComponents == UriComponents.Scheme)
                return m_Syntax.SchemeName; 
 
            // A serialzation info is "almost" the same as AbsoluteUri except for IPv6 + ScopeID hostname case
            if ((uriComponents & UriComponents.SerializationInfoString) != 0) 
                uriComponents |= UriComponents.AbsoluteUri;

            //This will get all the offsets, HostString will be created below if needed
            EnsureParseRemaining(); 

            //Check to see if we need the host/authotity string 
            if ((uriComponents & UriComponents.Host) != 0) 
                EnsureHostString(true);
 
            //This, single Port request is always processed here
            if (uriComponents == UriComponents.Port || uriComponents == UriComponents.StrongPort)
            {
                if (((m_Flags & Flags.NotDefaultPort) != 0) || (uriComponents == UriComponents.StrongPort && m_Syntax.DefaultPort != UriParser.NoDefaultPort)) 
                {
                    // recreate string from the port value 
                    return m_Info.Offset.PortValue.ToString(CultureInfo.InvariantCulture); 
                }
                return string.Empty; 
            }

            if ((uriComponents & UriComponents.StrongPort) != 0)
            { 
                // Down the path we rely on Port to be ON for StrongPort
                uriComponents |= UriComponents.Port; 
            } 

            //This request sometime is faster to process here 
            if (uriComponents == UriComponents.Host && (uriFormat == UriFormat.UriEscaped
                || (( m_Flags & (Flags.HostNotCanonical | Flags.E_HostNotCanonical)) == 0)))
            {
                EnsureHostString(false); 
                return m_Info.Host;
            } 
 
            switch (uriFormat)
            { 
                case UriFormat.UriEscaped:
                    return GetEscapedParts(uriComponents);

                case V1ToStringUnescape: 
                case UriFormat.SafeUnescaped:
                case UriFormat.Unescaped: 
                    return GetUnescapedParts(uriComponents, uriFormat); 

                default: 
                    throw new ArgumentOutOfRangeException("uriFormat");
            }
        }
        // 
        //
        // 
        internal bool IsBaseOfHelper(Uri uriLink) 
        {
            //TO 
            if (!IsAbsoluteUri || UserDrivenParsing)
                return false;

            if (!uriLink.IsAbsoluteUri) 
            {
                //a relative uri could have quite tricky form, it's better to fix it now. 
                string newUriString = null; 
                UriFormatException e;
                bool dontEscape = false; 

                uriLink = ResolveHelper(this, uriLink, ref newUriString, ref dontEscape, out e);
                if (e != null)
                    return false; 

                if ((object)uriLink == null) 
                    uriLink = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e); 

                if (e != null) 
                    return false;
            }

            if (Syntax.SchemeName != uriLink.Syntax.SchemeName) 
                return false;
 
            // Canonicalize and test for substring match up to the last path slash 
            string me = GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped);
            string she = uriLink.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped); 

            unsafe
            {
                fixed (char* pMe = me) 
                {
                    fixed (char* pShe = she) 
                    { 
                        return TestForSubPath(pMe, (ushort)me.Length, pShe, (ushort)she.Length, IsUncOrDosPath || uriLink.IsUncOrDosPath);
                    } 
                }
            }
        }
        // 
        // Only a ctor time call
        // 
        private void CreateThisFromUri(Uri otherUri) 
        {
            // Clone the other guy but develop own UriInfo member 
            m_Info = null;

            m_Flags = otherUri.m_Flags;
            if (InFact(Flags.MinimalUriInfoSet)) 
            {
                m_Flags &= ~(Flags.MinimalUriInfoSet | Flags.AllUriInfoSet | Flags.IndexMask); 
                // Port / Path offset 
                int portIndex = otherUri.m_Info.Offset.Path;
                if (InFact(Flags.NotDefaultPort)) 
                {
                    // Find the start of the port.  Account for non-canonical ports like :00123
                    while (otherUri.m_String[portIndex] != ':' && portIndex > otherUri.m_Info.Offset.Host)
                    { 
                        portIndex--;
                    } 
                    if (otherUri.m_String[portIndex] != ':') 
                    {
                        // Something wrong with the NotDefaultPort flag.  Reset to path index 
                        System.Net.GlobalLog.Assert("Uri failed to locate custom port at index: " + portIndex);
                        portIndex = otherUri.m_Info.Offset.Path;
                    }
                } 
                m_Flags |= (Flags)portIndex; // Port or path
            } 
 
            m_Syntax = otherUri.m_Syntax;
            m_String = otherUri.m_String; 
            m_iriParsing = otherUri.m_iriParsing;
            if (otherUri.OriginalStringSwitched){
                m_originalUnicodeString = otherUri.m_originalUnicodeString;
            } 
            if (otherUri.AllowIdn && (otherUri.InFact(Flags.IdnHost) || otherUri.InFact(Flags.UnicodeHost))){
                m_DnsSafeHost = otherUri.m_DnsSafeHost; 
            } 
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

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