The Labs \ Source Viewer \ SSCLI \ System \ UnescapeMode

  1. //------------------------------------------------------------------------------
  2. // <copyright file="UriExt.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. /*++
  16. Abstract:
  17.     Uri extensibility model Implementation.
  18.     This file utilizes partial class feature.
  19.     Uri.cs file contains core System.Uri functionality.
  20. Author:
  21.     Alexei Vopilov    Nov 21 2003
  22. Revision History:
  23. --*/
  24. namespace System
  25. {
  26.     using System.Collections.Generic;
  27.     using System.Configuration;
  28.     using System.Globalization;
  29.     using System.Net.Configuration;
  30.     using System.Security.Permissions;
  31.     using System.Text;
  32.    
  33.    
  34.     public partial class Uri
  35.     {
  36.         //
  37.         // All public ctors go through here
  38.         //
  39.         private void CreateThis(string uri, bool dontEscape, UriKind uriKind)
  40.         {
  41.             // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow to be used here.
  42.             if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative) {
  43.                 throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind));
  44.             }
  45.            
  46.             m_String = (object)uri == null ? string.Empty : uri;
  47.            
  48.             if (dontEscape)
  49.                 m_Flags |= Flags.UserEscaped;
  50.            
  51.             ParsingError err = ParseScheme(m_String, ref m_Flags, ref m_Syntax);
  52.             UriFormatException e;
  53.            
  54.             InitializeUri(err, uriKind, out e);
  55.             if (e != null)
  56.                 throw e;
  57.         }
  58.         //
  59.         private void InitializeUri(ParsingError err, UriKind uriKind, out UriFormatException e)
  60.         {
  61.             if (err == ParsingError.None) {
  62.                 if (IsImplicitFile) {
  63.                     #if !PLATFORM_UNIX
  64.                     #endif // !PLATFORM_UNIX
  65.                    
  66.                     if (NotAny(Flags.DosPath) && uriKind != UriKind.Absolute && (uriKind == UriKind.Relative || (m_String.Length >= 2 && (m_String[0] != '\\' || m_String[1] != '\\')))) {
  67.                         m_Syntax = null;
  68.                         //make it be relative Uri
  69.                         m_Flags &= Flags.UserEscaped;
  70.                         // the only flag that makes sense for a relative uri
  71.                         e = null;
  72.                         return;
  73.                         // Otheriwse an absolute file Uri wins when it's of the form "\\something"
  74.                     }
  75.                     #if !PLATFORM_UNIX
  76.                     else if (uriKind == UriKind.Relative && InFact(Flags.DosPath)) {
  77.                         m_Syntax = null;
  78.                         //make it be relative Uri
  79.                         m_Flags &= Flags.UserEscaped;
  80.                         // the only flag that makes sense for a relative uri
  81.                         e = null;
  82.                         return;
  83.                         // Otheriwse an absolute file Uri wins when it's of the form "c:\something"
  84.                     }
  85.                     #endif // !PLATFORM_UNIX
  86.                 }
  87.             }
  88.             else if (err > ParsingError.LastRelativeUriOkErrIndex) {
  89.                 //This is a fatal error based solely on scheme name parsing
  90.                 m_String = null;
  91.                 // make it be invalid Uri
  92.                 e = GetException(err);
  93.                 return;
  94.             }
  95.            
  96.             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);
  97.             //
  98.             //
  99.             //
  100.             if (m_Syntax != null) {
  101.                 if (m_Syntax.IsSimple) {
  102.                     if ((err = PrivateParseMinimal()) != ParsingError.None) {
  103.                         if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex) {
  104.                             m_Syntax = null;
  105.                             // convert to relative uri
  106.                             e = null;
  107.                             m_Flags &= Flags.UserEscaped;
  108.                             // the only flag that makes sense for a relative uri
  109.                         }
  110.                         else
  111.                             e = GetException(err);
  112.                     }
  113.                     else if (uriKind == UriKind.Relative) {
  114.                         // Here we know that we can create an absolute Uri, but the user has requested only a relative one
  115.                         e = GetException(ParsingError.CannotCreateRelative);
  116.                     }
  117.                     else
  118.                         e = null;
  119.                     // will return from here
  120.                 }
  121.                 else {
  122.                     // offer custom parser to create a parsing context
  123.                     m_Syntax = m_Syntax.InternalOnNewUri();
  124.                    
  125.                     // incase they won't call us
  126.                     m_Flags |= Flags.UserDrivenParsing;
  127.                    
  128.                     // Ask a registered type to validate this uri
  129.                     m_Syntax.InternalValidate(this, out e);
  130.                    
  131.                     if (e != null) {
  132.                         // Can we still take it as a relative Uri?
  133.                         if (uriKind != UriKind.Absolute && err != ParsingError.None && err <= ParsingError.LastRelativeUriOkErrIndex) {
  134.                             m_Syntax = null;
  135.                             // convert it to relative
  136.                             e = null;
  137.                             m_Flags &= Flags.UserEscaped;
  138.                             // the only flag that makes sense for a relative uri
  139.                         }
  140.                     }
  141.                     // e == null
  142.                     else {
  143.                         if (err != ParsingError.None || InFact(Flags.ErrorOrParsingRecursion)) {
  144.                             // User parser took over on an invalid Uri
  145.                             SetUserDrivenParsing();
  146.                         }
  147.                         else if (uriKind == UriKind.Relative) {
  148.                             // Here we know that custom parser can create an absolute Uri, but the user has requested only a relative one
  149.                             e = GetException(ParsingError.CannotCreateRelative);
  150.                         }
  151.                     }
  152.                     // will return from here
  153.                 }
  154.             }
  155.             else if (err != ParsingError.None && uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex) {
  156.                 e = null;
  157.                 m_Flags &= Flags.UserEscaped;
  158.                 // the only flag that makes sense for a relative uri
  159.             }
  160.             else {
  161.                 m_String = null;
  162.                 // make it be invalid Uri
  163.                 e = GetException(err);
  164.             }
  165.         }
  166.         //
  167.         //
  168.         // Returns true if the string represents a valid argument to the Uri ctor
  169.         // If uriKind != AbsoluteUri then certain parsing erros are ignored but Uri usage is limited
  170.         //
  171.         public static bool TryCreate(string uriString, UriKind uriKind, out Uri result)
  172.         {
  173.             if ((object)uriString == null) {
  174.                 result = null;
  175.                 return false;
  176.             }
  177.             UriFormatException e = null;
  178.             result = CreateHelper(uriString, false, uriKind, ref e);
  179.             return (object)e == null && result != null;
  180.         }
  181.         //
  182.         public static bool TryCreate(Uri baseUri, string relativeUri, out Uri result)
  183.         {
  184.             Uri relativeLink;
  185.             if (TryCreate(relativeUri, UriKind.RelativeOrAbsolute, out relativeLink)) {
  186.                 if (!relativeLink.IsAbsoluteUri)
  187.                     return TryCreate(baseUri, relativeLink, out result);
  188.                
  189.                 result = relativeLink;
  190.                 return true;
  191.             }
  192.             result = null;
  193.             return false;
  194.         }
  195.         //
  196.         public static bool TryCreate(Uri baseUri, Uri relativeUri, out Uri result)
  197.         {
  198.             result = null;
  199.            
  200.             //Consider: Work out the baseUri==null case
  201.             if ((object)baseUri == null)
  202.                 return false;
  203.            
  204.             if (baseUri.IsNotAbsoluteUri)
  205.                 return false;
  206.            
  207.             UriFormatException e;
  208.             string newUriString = null;
  209.            
  210.             bool dontEscape;
  211.             if (baseUri.Syntax.IsSimple) {
  212.                 dontEscape = relativeUri.UserEscaped;
  213.                 result = ResolveHelper(baseUri, relativeUri, ref newUriString, ref dontEscape, out e);
  214.             }
  215.             else {
  216.                 dontEscape = false;
  217.                 newUriString = baseUri.Syntax.InternalResolve(baseUri, relativeUri, out e);
  218.             }
  219.            
  220.             if (e != null)
  221.                 return false;
  222.            
  223.             if ((object)result == null)
  224.                 result = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e);
  225.            
  226.             return (object)e == null && result != null && result.IsAbsoluteUri;
  227.         }
  228.         //
  229.         //
  230.         public bool IsBaseOf(Uri uri)
  231.         {
  232.             if (!IsAbsoluteUri)
  233.                 return false;
  234.            
  235.             if (Syntax.IsSimple)
  236.                 return IsBaseOfHelper(uri);
  237.            
  238.             return Syntax.InternalIsBaseOf(this, uri);
  239.         }
  240.         //
  241.         //
  242.         public string GetComponents(UriComponents components, UriFormat format)
  243.         {
  244.             if (((components & UriComponents.SerializationInfoString) != 0) && components != UriComponents.SerializationInfoString)
  245.                 throw new ArgumentOutOfRangeException("UriComponents.SerializationInfoString");
  246.            
  247.             if ((format & ~UriFormat.SafeUnescaped) != 0)
  248.                 throw new ArgumentOutOfRangeException("format");
  249.            
  250.             if (IsNotAbsoluteUri) {
  251.                 if (components == UriComponents.SerializationInfoString)
  252.                     return GetRelativeSerializationString(format);
  253.                 else
  254.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  255.             }
  256.            
  257.             if (Syntax.IsSimple)
  258.                 return GetComponentsHelper(components, format);
  259.            
  260.             return Syntax.InternalGetComponents(this, components, format);
  261.         }
  262.         //
  263.         public bool IsWellFormedOriginalString()
  264.         {
  265.             if (IsNotAbsoluteUri || Syntax.IsSimple)
  266.                 return InternalIsWellFormedOriginalString();
  267.            
  268.             return Syntax.InternalIsWellFormedOriginalString(this);
  269.         }
  270.         public static bool IsWellFormedUriString(string uriString, UriKind uriKind)
  271.         {
  272.             Uri result;
  273.            
  274.             if (!Uri.TryCreate(uriString, uriKind, out result))
  275.                 return false;
  276.            
  277.             return result.IsWellFormedOriginalString();
  278.         }
  279.         //
  280.         //
  281.         // This is for languages that do not support == != operators overloading
  282.         //
  283.         // Note that Uri.Equals will get an optimized path but is limited to true/fasle result only
  284.         //
  285.         public static int Compare(Uri uri1, Uri uri2, UriComponents partsToCompare, UriFormat compareFormat, StringComparison comparisonType)
  286.         {
  287.            
  288.             if ((object)uri1 == null) {
  289.                 if (uri2 == null)
  290.                     return 0;
  291.                 // Equal
  292.                 return -1;
  293.                 // null < non-null
  294.             }
  295.             if ((object)uri2 == null)
  296.                 return 1;
  297.             // non-null > null
  298.             // a relative uri is always less than an absolute one
  299.             if (!uri1.IsAbsoluteUri || !uri2.IsAbsoluteUri)
  300.                 return uri1.IsAbsoluteUri ? 1 : uri2.IsAbsoluteUri ? -1 : string.Compare(uri1.OriginalString, uri2.OriginalString, comparisonType);
  301.            
  302.             return string.Compare(uri1.GetParts(partsToCompare, compareFormat), uri2.GetParts(partsToCompare, compareFormat), comparisonType);
  303.         }
  304.         //
  305.         //
  306.         //
  307.         public static string UnescapeDataString(string stringToUnescape)
  308.         {
  309.             if ((object)stringToUnescape == null)
  310.                 throw new ArgumentNullException("stringToUnescape");
  311.            
  312.             if (stringToUnescape.Length == 0)
  313.                 return string.Empty;
  314.            
  315.             unsafe {
  316.                 fixed (char* pStr = stringToUnescape) {
  317.                     int position;
  318.                     for (position = 0; position < stringToUnescape.Length; ++position)
  319.                         if (pStr[position] == '%')
  320.                             break;
  321.                    
  322.                     if (position == stringToUnescape.Length)
  323.                         return stringToUnescape;
  324.                    
  325.                     position = 0;
  326.                     char[] dest = new char[stringToUnescape.Length];
  327.                     dest = UnescapeString(stringToUnescape, 0, stringToUnescape.Length, dest, ref position, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.Unescape | UnescapeMode.UnescapeAllOrThrow);
  328.                     return new string(dest, 0, position);
  329.                 }
  330.             }
  331.         }
  332.         //
  333.         // Where stringToEscape is intented to be a completely unescaped URI string.
  334.         // This method will escape any character that is not a reserved or unreserved character, including percent signs.
  335.         // Note that EscapeUriString will also do not escape a '#' sign.
  336.         //
  337.         public static string EscapeUriString(string stringToEscape)
  338.         {
  339.             if ((object)stringToEscape == null)
  340.                 throw new ArgumentNullException("stringToUnescape");
  341.            
  342.             if (stringToEscape.Length == 0)
  343.                 return string.Empty;
  344.            
  345.             int position = 0;
  346.             char[] dest = EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, true, c_DummyChar, c_DummyChar, c_DummyChar);
  347.             if ((object)dest == null)
  348.                 return stringToEscape;
  349.             return new string(dest, 0, position);
  350.         }
  351.         //
  352.         // Where stringToEscape is intended to be URI data, but not an entire URI.
  353.         // This method will escape any character that is not an unreserved character, including percent signs.
  354.         //
  355.         public static string EscapeDataString(string stringToEscape)
  356.         {
  357.             if ((object)stringToEscape == null)
  358.                 throw new ArgumentNullException("stringToUnescape");
  359.            
  360.             if (stringToEscape.Length == 0)
  361.                 return string.Empty;
  362.            
  363.             int position = 0;
  364.             char[] dest = EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, false, c_DummyChar, c_DummyChar, c_DummyChar);
  365.             if (dest == null)
  366.                 return stringToEscape;
  367.             return new string(dest, 0, position);
  368.         }
  369.        
  370.         // - forceX characters are always escaped if found
  371.         // - rsvd character will remain unescaped
  372.         //
  373.         // start - starting offset from input
  374.         // end - the exclusive ending offset in input
  375.         // destPos - starting offset in dest for output, on return this will be an exclusive "end" in the output.
  376.         //
  377.         // In case "dest" has lack of space it will be reallocated by preserving the _whole_ content up to current destPos
  378.         //
  379.         // Returns null if nothing has to be escaped AND passed dest was null, otherwise the resulting array with the updated destPos
  380.         //
  381.         const short c_MaxAsciiCharsReallocate = 40;
  382.         const short c_MaxUnicodeCharsReallocate = 40;
  383.         const short c_MaxUTF_8BytesPerUnicodeChar = 4;
  384.         const short c_EncodedCharsPerByte = 3;
  385.         unsafe private static char[] EscapeString(string input, int start, int end, char[] dest, ref int destPos, bool isUriString, char force1, char force2, char rsvd)
  386.         {
  387.             if (end - start >= c_MaxUriBufferSize)
  388.                 throw GetException(ParsingError.SizeLimit);
  389.            
  390.             int i = start;
  391.             int prevInputPos = start;
  392.             byte* bytes = stackalloc byte[c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar];
  393.             // 40*4=160
  394.             fixed (char* pStr = input) {
  395.                 for (; i < end; ++i) {
  396.                     char ch = pStr[i];
  397.                    
  398.                     // a Unicode ?
  399.                     if (ch > '\u127') {
  400.                         short maxSize = (short)Math.Min(end - i, (int)c_MaxUnicodeCharsReallocate - 1);
  401.                        
  402.                         short count = 1;
  403.                         for (; count < maxSize && pStr[i + count] > '\u127'; ++count)
  404.                             ;
  405.                        
  406.                         // Is the last a high surrogate?
  407.                         if (pStr[i + count - 1] >= 55296 && pStr[i + count - 1] <= 56319) {
  408.                             // Should be a rare case where the app tries to feed an invalid Unicode surrogates pair
  409.                             if (count == 1 || count == end - i)
  410.                                 throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
  411.                             // need to grab one more char as a Surrogate except when it's a bogus input
  412.                             ++count;
  413.                         }
  414.                        
  415.                         dest = EnsureDestinationSize(pStr, dest, i, (short)(count * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte), c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar * c_EncodedCharsPerByte, ref destPos, prevInputPos);
  416.                        
  417.                         short numberOfBytes = (short)Encoding.UTF8.GetBytes(pStr + i, count, bytes, c_MaxUnicodeCharsReallocate * c_MaxUTF_8BytesPerUnicodeChar);
  418.                        
  419.                         // This is the only exception that built in UriParser can throw after a Uri ctor.
  420.                         // Should not happen unless the app tries to feed an invalid Unicode String
  421.                         if (numberOfBytes == 0)
  422.                             throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
  423.                        
  424.                         i += (count - 1);
  425.                        
  426.                         for (count = 0; count < numberOfBytes; ++count)
  427.                             EscapeAsciiChar((char)bytes[count], dest, ref destPos);
  428.                        
  429.                         prevInputPos = i + 1;
  430.                     }
  431.                     else if (ch == '%' && rsvd == '%') {
  432.                         // Means we don't reEncode '%' but check for the possible escaped sequence
  433.                         dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
  434.                         if (i + 2 < end && EscapedAscii(pStr[i + 1], pStr[i + 2]) != c_DummyChar) {
  435.                             // leave it escaped
  436.                             dest[destPos++] = '%';
  437.                             dest[destPos++] = pStr[i + 1];
  438.                             dest[destPos++] = pStr[i + 2];
  439.                             i += 2;
  440.                         }
  441.                         else {
  442.                             EscapeAsciiChar('%', dest, ref destPos);
  443.                         }
  444.                         prevInputPos = i + 1;
  445.                     }
  446.                     else if (ch == force1 || ch == force2) {
  447.                         dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
  448.                         EscapeAsciiChar(ch, dest, ref destPos);
  449.                         prevInputPos = i + 1;
  450.                     }
  451.                     else if (ch != rsvd && (isUriString ? IsNotReservedNotUnreservedNotHash(ch) : IsNotUnreserved(ch))) {
  452.                         dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
  453.                         EscapeAsciiChar(ch, dest, ref destPos);
  454.                         prevInputPos = i + 1;
  455.                     }
  456.                 }
  457.                
  458.                 if (prevInputPos != i) {
  459.                     // need to fill up the dest array ?
  460.                     if (prevInputPos != start || dest != null)
  461.                         dest = EnsureDestinationSize(pStr, dest, i, 0, 0, ref destPos, prevInputPos);
  462.                 }
  463.             }
  464.            
  465.             return dest;
  466.         }
  467.         //
  468.         // Escapes ASCII only ch1, ch2 or ch3 return null if input is already good
  469.         //
  470.         unsafe private static char[] EscapeOnly(string input, int start, int end, ref int destPos, char ch1, char ch2, char ch3)
  471.         {
  472.             char[] dest = null;
  473.             int i = start;
  474.             int prevInputPos = start;
  475.            
  476.             fixed (char* pStr = input) {
  477.                 for (; i < end; ++i) {
  478.                     char ch = pStr[i];
  479.                     if (ch == ch1 || ch == ch2 || ch == ch3) {
  480.                         dest = EnsureDestinationSize(pStr, dest, i, c_EncodedCharsPerByte, c_MaxAsciiCharsReallocate * c_EncodedCharsPerByte, ref destPos, prevInputPos);
  481.                         EscapeAsciiChar(ch, dest, ref destPos);
  482.                         prevInputPos = i + 1;
  483.                     }
  484.                 }
  485.                 if (prevInputPos != i) {
  486.                     // need to fill up the dest array ?
  487.                     if (prevInputPos != start || dest != null)
  488.                         dest = EnsureDestinationSize(pStr, dest, i, 0, 0, ref destPos, prevInputPos);
  489.                 }
  490.             }
  491.             return dest;
  492.         }
  493.        
  494.         //
  495.         // ensure destination array has enough space and contains all the needed input stuff
  496.         //
  497.         unsafe private static char[] EnsureDestinationSize(char* pStr, char[] dest, int currentInputPos, short charsToAdd, short minReallocateChars, ref int destPos, int prevInputPos)
  498.         {
  499.             if ((object)dest == null || dest.Length < destPos + (currentInputPos - prevInputPos) + charsToAdd) {
  500.                 // allocating or reallocating array by ensuring enough space based on maxCharsToAdd.
  501.                 char[] newresult = new char[destPos + (currentInputPos - prevInputPos) + minReallocateChars];
  502.                
  503.                 if ((object)dest != null && destPos != 0)
  504.                     Buffer.BlockCopy(dest, 0, newresult, 0, destPos << 1);
  505.                 dest = newresult;
  506.             }
  507.            
  508.             // ensuring we copied everything form the input string left before last escaping
  509.             while (prevInputPos != currentInputPos)
  510.                 dest[destPos++] = pStr[prevInputPos++];
  511.             return dest;
  512.         }
  513.         //
  514.         // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
  515.         // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
  516.         // excluded = control | space | delims | unwise
  517.         // delims = "<" | ">" | "#" | "%" | <">
  518.         // unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"
  519.         //
  520.         unsafe private static bool IsNotReservedNotUnreservedNotHash(char c)
  521.         {
  522.             if (c > 'z' && c != '~') {
  523.                 return true;
  524.             }
  525.             else if (c > 'Z' && c < 'a' && c != '_') {
  526.                 return true;
  527.             }
  528.             else if (c < '!') {
  529.                 return true;
  530.             }
  531.             else if (c == '>' || c == '<' || c == '%' || c == '"' || c == '`') {
  532.                 return true;
  533.             }
  534.             return false;
  535.         }
  536.         //
  537.         unsafe private static bool IsNotUnreserved(char c)
  538.         {
  539.             if (c > 'z' && c != '~') {
  540.                 return true;
  541.             }
  542.             else if ((c > '9' && c < 'A') || (c > 'Z' && c < 'a' && c != '_')) {
  543.                 return true;
  544.             }
  545.             else if (c < '\'' && c != '!') {
  546.                 return true;
  547.             }
  548.             else if (c == '+' || c == ',' || c == '/') {
  549.                 return true;
  550.             }
  551.             return false;
  552.         }
  553.        
  554.         //
  555.         // This method will assume that any good Escaped Sequence will be unescaped in the output
  556.         // - Assumes Dest.Length - detPosition >= end-start
  557.         // - UnescapeLevel controls various modes of opearion
  558.         // - Any "bad" escape sequence will remain as is or '%' will be escaped.
  559.         // - destPosition tells the starting index in dest for placing the result.
  560.         // On return destPosition tells the last character + 1 postion in the "dest" array.
  561.         // - The control chars and chars passed in rsdvX parameters may be re-escaped depending on UnescapeLevel
  562.         // - It is a RARE case when Unescape actually needs escaping some characteres mentioned above.
  563.         // For this reason it returns a char[] that is usually the same ref as the input "dest" value.
  564.         //
  565.         [Flags()]
  566.         private enum UnescapeMode
  567.         {
  568.             CopyOnly = 0,
  569.             // used for V1.0 ToString() compatibility mode only
  570.             Escape = 1,
  571.             // Only used by ImplicitFile, the string is already fully unescaped
  572.             Unescape = 2,
  573.             // Only used as V1.0 UserEscaped compatibility mode
  574.             EscapeUnescape = Unescape | Escape,
  575.             // does both escaping control+reserved and unescaping of safe characters
  576.             V1ToStringFlag = 4,
  577.             // Only used as V1.0 ToString() compatibility mode, assumes DontEscape level also
  578.             UnescapeAll = 8,
  579.             // just unescape everything, leave bad escaped sequences as is (should throw but can't due to v1.0 compat)
  580.             UnescapeAllOrThrow = 16 | UnescapeAll
  581.             // just unescape everything plus throw on bad escaped sequences
  582.         }
  583.         unsafe private static char[] UnescapeString(string input, int start, int end, char[] dest, ref int destPosition, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode)
  584.         {
  585.             fixed (char* pStr = input) {
  586.                 return UnescapeString(pStr, start, end, dest, ref destPosition, rsvd1, rsvd2, rsvd3, unescapeMode);
  587.             }
  588.         }
  589.         unsafe private static char[] UnescapeString(char* pStr, int start, int end, char[] dest, ref int destPosition, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode)
  590.         {
  591.             byte[] bytes = null;
  592.             byte escapedReallocations = 0;
  593.             bool escapeReserved = false;
  594.             int next = start;
  595.            
  596.             while (true) {
  597.                 // we may need to re-pin dest[]
  598.                 fixed (char* pDest = dest) {
  599.                     if ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.CopyOnly) {
  600.                         while (start < end)
  601.                             pDest[destPosition++] = pStr[start++];
  602.                         return dest;
  603.                     }
  604.                    
  605.                     while (true) {
  606.                         char ch = (char)0;
  607.                        
  608.                         for (; next < end; ++next) {
  609.                             if ((ch = pStr[next]) == '%') {
  610.                                 if ((unescapeMode & UnescapeMode.Unescape) == 0) {
  611.                                     // re-escape, don't check anything else
  612.                                     escapeReserved = true;
  613.                                 }
  614.                                 else if (next + 2 < end) {
  615.                                     ch = EscapedAscii(pStr[next + 1], pStr[next + 2]);
  616.                                     // Unescape a good sequence if full unescape is requested
  617.                                     if (unescapeMode >= UnescapeMode.UnescapeAll) {
  618.                                         if (ch == c_DummyChar) {
  619.                                             if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) {
  620.                                                 // Should be a rare case where the app tries to feed an invalid escaped sequence
  621.                                                 throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
  622.                                             }
  623.                                             continue;
  624.                                         }
  625.                                     }
  626.                                     // re-escape % from an invalid sequence
  627.                                     else if (ch == c_DummyChar) {
  628.                                         if ((unescapeMode & UnescapeMode.Escape) != 0)
  629.                                             escapeReserved = true;
  630.                                         else
  631.                                             continue;
  632.                                         // we should throw instead but since v1.0 woudl just print '%'
  633.                                     }
  634.                                     // Do not unescape '%' itself unless full unescape is requested
  635.                                     else if (ch == '%') {
  636.                                         next += 2;
  637.                                         continue;
  638.                                     }
  639.                                     // Do not unescape a reserved char unless full unescape is requested
  640.                                     else if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3) {
  641.                                         next += 2;
  642.                                         continue;
  643.                                     }
  644.                                     // Do not unescape a dangerous char unless it's V1ToStringFlags mode
  645.                                     else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && IsNotSafeForUnescape(ch)) {
  646.                                         next += 2;
  647.                                         continue;
  648.                                     }
  649.                                     // unescape escaped char or escape %
  650.                                     break;
  651.                                 }
  652.                                 else if (unescapeMode >= UnescapeMode.UnescapeAll) {
  653.                                     if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) {
  654.                                         // Should be a rare case where the app tries to feed an invalid escaped sequence
  655.                                         throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
  656.                                     }
  657.                                     // keep a '%' as part of a bogus sequence (we should throw but this is how 1.0 has shipped)
  658.                                     continue;
  659.                                 }
  660.                                 else {
  661.                                     escapeReserved = true;
  662.                                 }
  663.                                 // escape (escapeReserved==ture) or otheriwse unescape the sequence
  664.                                 break;
  665.                             }
  666.                             else if ((unescapeMode & (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) == (UnescapeMode.Unescape | UnescapeMode.UnescapeAll)) {
  667.                                 continue;
  668.                             }
  669.                             else if ((unescapeMode & UnescapeMode.Escape) != 0) {
  670.                                 // Could actually escape some of the characters
  671.                                 if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3) {
  672.                                     // found an unescaped reserved character -> escape it
  673.                                     escapeReserved = true;
  674.                                     break;
  675.                                 }
  676.                                 else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && (ch <= '\u31' || (ch >= '\u127' && ch <= '\u159'))) {
  677.                                     // found an unescaped reserved character -> escape it
  678.                                     escapeReserved = true;
  679.                                     break;
  680.                                 }
  681.                             }
  682.                         }
  683.                        
  684.                         //copy off previous characters from input
  685.                         while (start < next)
  686.                             pDest[destPosition++] = pStr[start++];
  687.                        
  688.                         if (next != end) {
  689.                             if (escapeReserved) {
  690.                                 //escape that char
  691.                                 // Since this should be _really_ rare case, reallocate with constant size increase of 30 rsvd-type characters.
  692.                                 if (escapedReallocations == 0) {
  693.                                     escapedReallocations = 30;
  694.                                     char[] newDest = new char[dest.Length + escapedReallocations * 3];
  695.                                     fixed (char* pNewDest = newDest) {
  696.                                         for (int i = 0; i < destPosition; ++i)
  697.                                             pNewDest[i] = pDest[i];
  698.                                     }
  699.                                     dest = newDest;
  700.                                     // re-pin new dest[] array
  701.                                     goto dest_fixed_loop_break;
  702.                                 }
  703.                                 else {
  704.                                     --escapedReallocations;
  705.                                     EscapeAsciiChar(pStr[next], dest, ref destPosition);
  706.                                     escapeReserved = false;
  707.                                     start = ++next;
  708.                                     continue;
  709.                                 }
  710.                             }
  711.                            
  712.                             // unescaping either one Ascii or possibly multiple Unicode
  713.                            
  714.                             if (ch <= '\u127') {
  715.                                 //ASCII
  716.                                 dest[destPosition++] = ch;
  717.                                 next += 3;
  718.                                 start = next;
  719.                                 continue;
  720.                             }
  721.                            
  722.                             // Unicode
  723.                            
  724.                             int byteCount = 1;
  725.                             // lazy initialization of max size, will reuse the array for next sequences
  726.                             if ((object)bytes == null)
  727.                                 bytes = new byte[end - next];
  728.                            
  729.                             bytes[0] = (byte)ch;
  730.                             next += 3;
  731.                             while (next < end) {
  732.                                 // Check on exit criterion
  733.                                 if ((ch = pStr[next]) != '%' || next + 2 >= end)
  734.                                     break;
  735.                                
  736.                                 // already made sure we have 3 characters in str
  737.                                 ch = EscapedAscii(pStr[next + 1], pStr[next + 2]);
  738.                                
  739.                                 //invalid hex sequence ?
  740.                                 if (ch == c_DummyChar)
  741.                                     break;
  742.                                 else if (ch < '\u128')
  743.                                     break;
  744.                                 // character is not part of a UTF-8 sequence ?
  745.                                 else {
  746.                                     //a UTF-8 sequence
  747.                                     bytes[byteCount++] = (byte)ch;
  748.                                     next += 3;
  749.                                 }
  750.                             }
  751.                            
  752.                             int charCount = Encoding.UTF8.GetChars(bytes, 0, byteCount, dest, destPosition);
  753.                            
  754.                             if (charCount != 0) {
  755.                                 destPosition += charCount;
  756.                                 start = next;
  757.                             }
  758.                             else {
  759.                                
  760.                                 if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow) {
  761.                                     // Should be a rare case where the app tries to feed an invalid escaped sequence
  762.                                     throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
  763.                                 }
  764.                                
  765.                                 next = start + 3;
  766.                                 start = next;
  767.                                 dest[destPosition++] = (char)bytes[0];
  768.                             }
  769.                         }
  770.                        
  771.                         if (next == end)
  772.                             goto done;
  773.                     }
  774.                     dest_fixed_loop_break:
  775.                     ;
  776.                 }
  777.             }
  778.             done:
  779.            
  780.             return dest;
  781.         }
  782.        
  783.         //
  784.         // Do not unescape these in safe mode:
  785.         // 1) reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
  786.         // 2) excluded = control | "#" | "%" | "\"
  787.         //
  788.         // That will still give plenty characters unescaped by SafeUnesced mode such as
  789.         // 1) Unicode characters
  790.         // 2) Unreserved = alphanum | "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
  791.         // 3) DelimitersAndUnwise = "<" | ">" | <"> | "{" | "}" | "|" | "^" | "[" | "]" | "`"
  792.         static bool IsNotSafeForUnescape(char ch)
  793.         {
  794.             if (ch <= '\u31' || (ch >= '\u127' && ch <= '\u159'))
  795.                 return true;
  796.             else if ((ch >= ';' && ch <= '@' && (ch | '\u2') != '>') || (ch >= '#' && ch <= '&') || ch == '+' || ch == ',' || ch == '/' || ch == '\\')
  797.                 return true;
  798.            
  799.             return false;
  800.         }
  801.        
  802.        
  803.         //
  804.         // Internal stuff
  805.         //
  806.        
  807.         // Returns false if OriginalString value
  808.         // (1) is not correctly escaped as per URI spec excluding intl UNC name case
  809.         // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file"
  810.         // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file"
  811.         // (4) or contains unescaped backslashes even if they will be treated
  812.         // as forward slashes like http:\\host/path\file or file:\\\c:\path
  813.         //
  814.         unsafe internal bool InternalIsWellFormedOriginalString()
  815.         {
  816.             if (UserDrivenParsing)
  817.                 throw new InvalidOperationException(SR.GetString(SR.net_uri_UserDrivenParsing, this.GetType().FullName));
  818.            
  819.             fixed (char* str = m_String) {
  820.                 ushort idx = 0;
  821.                 //
  822.                 // For a relative Uri we only care about escaping and backslashes
  823.                 //
  824.                 if (!IsAbsoluteUri)
  825.                     return (CheckCanonical(str, ref idx, (ushort)m_String.Length, c_EOL) & (Check.BackslashInPath | Check.EscapedCanonical)) == Check.EscapedCanonical;
  826.                
  827.                 //
  828.                 // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file"
  829.                 //
  830.                 if (IsImplicitFile)
  831.                     return false;
  832.                
  833.                 //This will get all the offsets, a Host name will be checked separatelly below
  834.                 EnsureParseRemaining();
  835.                
  836.                 Flags nonCanonical = (m_Flags & Flags.E_CannotDisplayCanonical);
  837.                 // User, Path, Query or Fragment may have some non escaped characters
  838.                 if (((nonCanonical & (Flags.E_UserNotCanonical | Flags.E_PathNotCanonical | Flags.E_QueryNotCanonical | Flags.E_FragmentNotCanonical)) != Flags.Zero))
  839.                     return false;
  840.                
  841.                 // checking on scheme:\\ or file:////
  842.                 if (InFact(Flags.AuthorityFound)) {
  843.                     idx = (ushort)(m_Info.Offset.Scheme + m_Syntax.SchemeName.Length + 2);
  844.                     if (idx >= m_Info.Offset.User || m_String[idx - 1] == '\\' || m_String[idx] == '\\')
  845.                         return false;
  846.                    
  847.                     #if !PLATFORM_UNIX
  848.                     if (InFact(Flags.UncPath | Flags.DosPath)) {
  849.                         while (++idx < m_Info.Offset.User && (m_String[idx] == '/' || m_String[idx] == '\\'))
  850.                             return false;
  851.                     }
  852.                     #endif // !PLATFORM_UNIX
  853.                 }
  854.                
  855.                
  856.                 // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file"
  857.                 // Note that for this check to be more general we assert that if Path is non empty and if it requires a first slash
  858.                 // (which looks absent) then the method has to fail.
  859.                 // Today it's only possible for a Dos like path, i.e. file://c:/bla would fail below check.
  860.                 if (InFact(Flags.FirstSlashAbsent) && m_Info.Offset.Query > m_Info.Offset.Path)
  861.                     return false;
  862.                
  863.                 // (4) or contains unescaped backslashes even if they will be treated
  864.                 // as forward slashes like http:\\host/path\file or file:\\\c:\path
  865.                 // Note we do not check for Flags.ShouldBeCompressed i.e. allow // /./ and alike as valid
  866.                 if (InFact(Flags.BackslashInPath))
  867.                     return false;
  868.                
  869.                 // Capturing a rare case like file:///c|/dir
  870.                 if (IsDosPath && m_String[m_Info.Offset.Path + SecuredPathIndex - 1] == '|')
  871.                     return false;
  872.                
  873.                 //
  874.                 // May need some real CPU processing to anwser the request
  875.                 //
  876.                 //
  877.                 // Check escaping for authority
  878.                 //
  879.                 if ((m_Flags & Flags.CanonicalDnsHost) == 0) {
  880.                     idx = m_Info.Offset.User;
  881.                     Check result = CheckCanonical(str, ref idx, (ushort)m_Info.Offset.Path, '/');
  882.                     if ((result & (Check.ReservedFound | Check.BackslashInPath | Check.EscapedCanonical)) != Check.EscapedCanonical)
  883.                         return false;
  884.                 }
  885.                
  886.                 // Want to ensure there are slashes after the scheme
  887.                 if ((m_Flags & (Flags.SchemeNotCanonical | Flags.AuthorityFound)) == (Flags.SchemeNotCanonical | Flags.AuthorityFound)) {
  888.                     idx = (ushort)m_Syntax.SchemeName.Length;
  889.                     while (str[idx++] != ':')
  890.                         ;
  891.                     if (idx + 1 >= m_String.Length || str[idx] != '/' || str[idx + 1] != '/')
  892.                         return false;
  893.                 }
  894.             }
  895.             //
  896.             // May be scheme, host, port or path need some canonicalization but still the uri string is found to be a "well formed" one
  897.             //
  898.             return true;
  899.         }
  900.        
  901.        
  902.         // Should never be used except by the below method
  903.         private Uri(Flags flags, UriParser uriParser, string uri)
  904.         {
  905.             m_Flags = flags;
  906.             m_Syntax = uriParser;
  907.             m_String = uri;
  908.         }
  909.         //
  910.         // a Uri.TryCreate() method goes through here.
  911.         //
  912.         static internal Uri CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException e)
  913.         {
  914.             // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow to be used here.
  915.             if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative) {
  916.                 throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind));
  917.             }
  918.            
  919.             UriParser syntax = null;
  920.             Flags flags = Flags.Zero;
  921.             ParsingError err = ParseScheme(uriString, ref flags, ref syntax);
  922.            
  923.             if (dontEscape)
  924.                 flags |= Flags.UserEscaped;
  925.            
  926.             // We won't use User factory for these errors
  927.             if (err != ParsingError.None) {
  928.                 // If it looks as a relative Uri, custom factory is ignored
  929.                 if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex)
  930.                     return new Uri((flags & Flags.UserEscaped), null, uriString);
  931.                
  932.                 return null;
  933.             }
  934.            
  935.             // Cannot be relative Uri if came here
  936.             Uri result = new Uri(flags, syntax, uriString);
  937.            
  938.             // Validate instance using ether built in or a user Parser
  939.             try {
  940.                 result.InitializeUri(err, uriKind, out e);
  941.                
  942.                 if (e == null)
  943.                     return result;
  944.                
  945.                 return null;
  946.             }
  947.             catch (UriFormatException ee) {
  948.                 System.Net.GlobalLog.Assert(!syntax.IsSimple, "A UriPraser threw on InitializeAndValidate.");
  949.                 e = ee;
  950.                 // A precaution since custom Parser should never throw in this case.
  951.                 return null;
  952.             }
  953.         }
  954.         //
  955.         // Resolves into either baseUri or relativeUri according to conditions OR if not possible it uses newUriString
  956.         // to return combined URI strings from both Uris
  957.         // otherwise if e != null on output the operation has failed
  958.         //
  959.        
  960.         static internal Uri ResolveHelper(Uri baseUri, Uri relativeUri, ref string newUriString, ref bool userEscaped, out UriFormatException e)
  961.         {
  962.             System.Net.GlobalLog.Assert(!baseUri.IsNotAbsoluteUri && !baseUri.UserDrivenParsing, "Uri::ResolveHelper()|baseUri is not Absolute or is controlled by User Parser.");
  963.            
  964.             e = null;
  965.             string relativeStr = string.Empty;
  966.            
  967.             if ((object)relativeUri != null) {
  968.                 if (relativeUri.IsAbsoluteUri)
  969.                     return relativeUri;
  970.                
  971.                 relativeStr = relativeUri.OriginalString;
  972.                 userEscaped = relativeUri.UserEscaped;
  973.             }
  974.             else
  975.                 relativeStr = string.Empty;
  976.            
  977.             // Here we can assert that passed "relativeUri" is indeed a relative one
  978.            
  979.             if (relativeStr.Length > 0 && (IsLWS(relativeStr[0]) || IsLWS(relativeStr[relativeStr.Length - 1])))
  980.                 relativeStr = relativeStr.Trim(_WSchars);
  981.            
  982.             if (relativeStr.Length == 0) {
  983.                 newUriString = baseUri.GetParts(UriComponents.AbsoluteUri, baseUri.UserEscaped ? UriFormat.UriEscaped : UriFormat.SafeUnescaped);
  984.                 return null;
  985.             }
  986.            
  987.             // Check for a simple fragment in relative part
  988.             if (relativeStr[0] == '#' && !baseUri.IsImplicitFile && baseUri.Syntax.InFact(UriSyntaxFlags.MayHaveFragment)) {
  989.                 newUriString = baseUri.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.UriEscaped) + relativeStr;
  990.                 return null;
  991.             }
  992.            
  993.             // Check on the DOS path in the relative Uri (a special case)
  994.             if (relativeStr.Length >= 3 && (relativeStr[1] == ':' || relativeStr[1] == '|') && IsAsciiLetter(relativeStr[0]) && (relativeStr[2] == '\\' || relativeStr[2] == '/')) {
  995.                
  996.                 if (baseUri.IsImplicitFile) {
  997.                     // It could have file:/// prepended to the result but we want to keep it as *Implicit* File Uri
  998.                     newUriString = relativeStr;
  999.                     return null;
  1000.                 }
  1001.                 else if (baseUri.Syntax.InFact(UriSyntaxFlags.AllowDOSPath)) {
  1002.                     // The scheme is not changed just the path gets replaced
  1003.                     string prefix;
  1004.                     if (baseUri.InFact(Flags.AuthorityFound))
  1005.                         prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":///" : "://";
  1006.                     else
  1007.                         prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":/" : ":";
  1008.                    
  1009.                     newUriString = baseUri.Scheme + prefix + relativeStr;
  1010.                     return null;
  1011.                 }
  1012.                 // If we are here then input like "http://host/path/" + "C:\x" will produce the result http://host/path/c:/x
  1013.             }
  1014.            
  1015.            
  1016.             ParsingError err = GetCombinedString(baseUri, relativeStr, userEscaped, ref newUriString);
  1017.            
  1018.             if (err != ParsingError.None) {
  1019.                 e = GetException(err);
  1020.                 return null;
  1021.             }
  1022.            
  1023.             if ((object)newUriString == (object)baseUri.m_String)
  1024.                 return baseUri;
  1025.            
  1026.             return null;
  1027.         }
  1028.        
  1029.         unsafe private string GetRelativeSerializationString(UriFormat format)
  1030.         {
  1031.             if (format == UriFormat.UriEscaped) {
  1032.                 if (m_String.Length == 0)
  1033.                     return string.Empty;
  1034.                 int position = 0;
  1035.                 char[] dest = EscapeString(m_String, 0, m_String.Length, null, ref position, true, c_DummyChar, c_DummyChar, '%');
  1036.                 if ((object)dest == null)
  1037.                     return m_String;
  1038.                 return new string(dest, 0, position);
  1039.             }
  1040.             else if (format == UriFormat.Unescaped)
  1041.                 return UnescapeDataString(m_String);
  1042.            
  1043.            
  1044.             else if (format == UriFormat.SafeUnescaped) {
  1045.                 if (m_String.Length == 0)
  1046.                     return string.Empty;
  1047.                
  1048.                 char[] dest = new char[m_String.Length];
  1049.                 int position = 0;
  1050.                 dest = UnescapeString(m_String, 0, m_String.Length, dest, ref position, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.EscapeUnescape);
  1051.                 return new string(dest, 0, position);
  1052.             }
  1053.             else
  1054.                 throw new ArgumentOutOfRangeException("format");
  1055.            
  1056.         }
  1057.        
  1058.         //
  1059.         // UriParser helpers methods
  1060.         //
  1061.         internal string GetComponentsHelper(UriComponents uriComponents, UriFormat uriFormat)
  1062.         {
  1063.             if (uriComponents == UriComponents.Scheme)
  1064.                 return m_Syntax.SchemeName;
  1065.            
  1066.             // A serialzation info is "almost" the same as AbsoluteUri except for IPv6 + ScopeID hostname case
  1067.             if ((uriComponents & UriComponents.SerializationInfoString) != 0)
  1068.                 uriComponents |= UriComponents.AbsoluteUri;
  1069.            
  1070.             //This will get all the offsets, HostString will be created below if needed
  1071.             EnsureParseRemaining();
  1072.            
  1073.             //Check to see if we need the host/authotity string
  1074.             if ((uriComponents & UriComponents.Host) != 0)
  1075.                 EnsureHostString(true);
  1076.            
  1077.             //This, single Port request is always processed here
  1078.             if (uriComponents == UriComponents.Port || uriComponents == UriComponents.StrongPort) {
  1079.                 if (InFact(Flags.NotDefaultPort) || (uriComponents == UriComponents.StrongPort && m_Syntax.DefaultPort != UriParser.NoDefaultPort)) {
  1080.                     // recreate string from the port value
  1081.                     return m_Info.Offset.PortValue.ToString(CultureInfo.InvariantCulture);
  1082.                 }
  1083.                 return string.Empty;
  1084.             }
  1085.            
  1086.             if ((uriComponents & UriComponents.StrongPort) != 0) {
  1087.                 // Down the path we rely on Port to be ON for StrongPort
  1088.                 uriComponents |= UriComponents.Port;
  1089.             }
  1090.            
  1091.             //This request sometime is faster to process here
  1092.             if (uriComponents == UriComponents.Host && (uriFormat == UriFormat.UriEscaped || NotAny(Flags.HostNotCanonical | Flags.E_HostNotCanonical))) {
  1093.                 EnsureHostString(false);
  1094.                 return m_Info.Host;
  1095.             }
  1096.            
  1097.             switch (uriFormat) {
  1098.                 case UriFormat.UriEscaped:
  1099.                     return GetEscapedParts(uriComponents);
  1100.                 case V1ToStringUnescape:
  1101.                 case UriFormat.SafeUnescaped:
  1102.                 case UriFormat.Unescaped:
  1103.                    
  1104.                     return GetUnescapedParts(uriComponents, uriFormat);
  1105.                 default:
  1106.                    
  1107.                     throw new ArgumentOutOfRangeException("uriFormat");
  1108.                     break;
  1109.             }
  1110.         }
  1111.         //
  1112.         //
  1113.         //
  1114.         internal bool IsBaseOfHelper(Uri uriLink)
  1115.         {
  1116.             if (!IsAbsoluteUri || UserDrivenParsing)
  1117.                 return false;
  1118.            
  1119.             if (!uriLink.IsAbsoluteUri) {
  1120.                 //a relative uri could have quite tricky form, it's better to fix it now.
  1121.                 string newUriString = null;
  1122.                 UriFormatException e;
  1123.                 bool dontEscape = false;
  1124.                
  1125.                 uriLink = ResolveHelper(this, uriLink, ref newUriString, ref dontEscape, out e);
  1126.                 if (e != null)
  1127.                     return false;
  1128.                
  1129.                 if ((object)uriLink == null)
  1130.                     uriLink = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e);
  1131.                
  1132.                 if (e != null)
  1133.                     return false;
  1134.             }
  1135.            
  1136.             if (Syntax.SchemeName != uriLink.Syntax.SchemeName)
  1137.                 return false;
  1138.            
  1139.             // Canonicalize and test for substring match up to the last path slash
  1140.             string me = GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped);
  1141.             string she = uriLink.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped);
  1142.            
  1143.             unsafe {
  1144.                 fixed (char* pMe = me) {
  1145.                     fixed (char* pShe = she) {
  1146.                         return TestForSubPath(pMe, (ushort)me.Length, pShe, (ushort)she.Length, IsUncOrDosPath || uriLink.IsUncOrDosPath);
  1147.                     }
  1148.                 }
  1149.             }
  1150.         }
  1151.         //
  1152.         // Only a ctor time call
  1153.         //
  1154.         private void CreateThisFromUri(Uri otherUri)
  1155.         {
  1156.             // Clone the other guy but develop own UriInfo member
  1157.             m_Info = null;
  1158.            
  1159.             m_Flags = otherUri.m_Flags;
  1160.             if (InFact(Flags.MinimalUriInfoSet)) {
  1161.                 m_Flags &= ~(Flags.MinimalUriInfoSet | Flags.AllUriInfoSet | Flags.IndexMask);
  1162.                 m_Flags |= (Flags)otherUri.m_Info.Offset.Path;
  1163.                
  1164.             }
  1165.            
  1166.             m_Syntax = otherUri.m_Syntax;
  1167.             m_String = otherUri.m_String;
  1168.         }
  1169.     }
  1170. }

Developer Fusion