The Labs \ Source Viewer \ SSCLI \ System \ Check

  1. //------------------------------------------------------------------------------
  2. // <copyright file="URI.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. namespace System
  16. {
  17.     using System.Configuration;
  18.     using System.Runtime.InteropServices;
  19.     using System.Text;
  20.     using System.Globalization;
  21.     using System.Runtime.Serialization;
  22.     using System.ComponentModel;
  23.     using System.Security.Permissions;
  24.     // using System.Threading;
  25.    
  26.     [Serializable()]
  27.     [TypeConverter(typeof(UriTypeConverter))]
  28.     public partial class Uri : ISerializable
  29.     {
  30.        
  31.         public static readonly string UriSchemeFile = UriParser.FileUri.SchemeName;
  32.         public static readonly string UriSchemeFtp = UriParser.FtpUri.SchemeName;
  33.         public static readonly string UriSchemeGopher = UriParser.GopherUri.SchemeName;
  34.         public static readonly string UriSchemeHttp = UriParser.HttpUri.SchemeName;
  35.         public static readonly string UriSchemeHttps = UriParser.HttpsUri.SchemeName;
  36.         public static readonly string UriSchemeMailto = UriParser.MailToUri.SchemeName;
  37.         public static readonly string UriSchemeNews = UriParser.NewsUri.SchemeName;
  38.         public static readonly string UriSchemeNntp = UriParser.NntpUri.SchemeName;
  39.         public static readonly string UriSchemeNetTcp = UriParser.NetTcpUri.SchemeName;
  40.         public static readonly string UriSchemeNetPipe = UriParser.NetPipeUri.SchemeName;
  41.         public static readonly string SchemeDelimiter = "://";
  42.        
  43.        
  44.         private const int c_Max16BitUtf8SequenceLength = 3 + 3 + 3 + 3;
  45.         //each unicode byte takes 3 escaped chars
  46.         private const int c_MaxUriBufferSize = 65520;
  47.         private const int c_MaxUriSchemeName = 1024;
  48.        
  49.         private string m_String;
  50.         // untouched user string
  51.         private UriParser m_Syntax;
  52.         // This is a whole Uri syntax, not only the scheme name
  53.         private enum ParsingError
  54.         {
  55.             // looks good
  56.             None = 0,
  57.            
  58.             // Could be only Relative
  59.             BadFormat = 1,
  60.             BadScheme = 2,
  61.             BadAuthority = 3,
  62.             EmptyUriString = 4,
  63.             LastRelativeUriOkErrIndex = 4,
  64.            
  65.             // Fatal
  66.             SchemeLimit = 5,
  67.             SizeLimit = 6,
  68.             MustRootedPath = 7,
  69.             LastFatalErrIndex = 7,
  70.            
  71.             // derived class controlled
  72.             BadHostName = 8,
  73.             NonEmptyHost = 9,
  74.             //unix only
  75.             BadPort = 10,
  76.             BadAuthorityTerminator = 11,
  77.            
  78.             CannotCreateRelative = 12
  79.         }
  80.        
  81.         [Flags()]
  82.         private enum Flags
  83.         {
  84.             Zero = 0,
  85.            
  86.             SchemeNotCanonical = 1,
  87.             UserNotCanonical = 2,
  88.             HostNotCanonical = 4,
  89.             PortNotCanonical = 8,
  90.             PathNotCanonical = 16,
  91.             QueryNotCanonical = 32,
  92.             FragmentNotCanonical = 64,
  93.             CannotDisplayCanonical = 127,
  94.            
  95.             E_UserNotCanonical = 128,
  96.             E_HostNotCanonical = 256,
  97.             E_PortNotCanonical = 512,
  98.             E_PathNotCanonical = 1024,
  99.             E_QueryNotCanonical = 2048,
  100.             E_FragmentNotCanonical = 4096,
  101.             E_CannotDisplayCanonical = 8064,
  102.            
  103.            
  104.             ShouldBeCompressed = 8192,
  105.             FirstSlashAbsent = 16384,
  106.             BackslashInPath = 32768,
  107.            
  108.             IndexMask = 65535,
  109.             HostTypeMask = 458752,
  110.             HostNotParsed = 0,
  111.             IPv6HostType = 65536,
  112.             IPv4HostType = 131072,
  113.             DnsHostType = 196608,
  114.             #if !PLATFORM_UNIX
  115.             UncHostType = 262144,
  116.             #endif // !PLATFORM_UNIX
  117.             BasicHostType = 327680,
  118.             UnusedHostType = 393216,
  119.             UnknownHostType = 458752,
  120.            
  121.             UserEscaped = 524288,
  122.             AuthorityFound = 1048576,
  123.             HasUserInfo = 2097152,
  124.             LoopbackHost = 4194304,
  125.             NotDefaultPort = 8388608,
  126.            
  127.             UserDrivenParsing = 16777216,
  128.             CanonicalDnsHost = 33554432,
  129.             ErrorOrParsingRecursion = 67108864,
  130.             // Used to signal a default parser error and alsoe to confirm Port and Host values in case of a custom user Parser
  131.             #if !PLATFORM_UNIX
  132.             DosPath = 134217728,
  133.             UncPath = 268435456,
  134.             #endif // !PLATFORM_UNIX
  135.             ImplicitFile = 536870912,
  136.             MinimalUriInfoSet = 1073741824,
  137.             AllUriInfoSet = unchecked((int)2147483648u)
  138.         }
  139.        
  140.         private Flags m_Flags;
  141.         private UriInfo m_Info;
  142.        
  143.         private class UriInfo
  144.         {
  145.             public string Host;
  146.             public string ScopeId;
  147.             //only IP v6 may need this
  148.             public string String;
  149.             public Offset Offset;
  150.             public MoreInfo MoreInfo;
  151.             //Multi-threading: This field must be always accessed through a _local_ stack copy of m_Info.
  152.         }
  153.        
  154.         [StructLayout(LayoutKind.Sequential, Pack = 1)]
  155.         private struct Offset
  156.         {
  157.             public ushort Scheme;
  158.             public ushort User;
  159.             public ushort Host;
  160.             public ushort PortValue;
  161.             public ushort Path;
  162.             public ushort Query;
  163.             public ushort Fragment;
  164.             public ushort End;
  165.         }
  166.        
  167.         private class MoreInfo
  168.         {
  169.             public string Path;
  170.             public string Query;
  171.             public string Fragment;
  172.             public string AbsoluteUri;
  173.             public int Hash;
  174.             public string RemoteUrl;
  175.         }
  176.        
  177.         private bool IsImplicitFile {
  178.             get { return (m_Flags & Flags.ImplicitFile) != 0; }
  179.         }
  180.        
  181.         private bool IsUncOrDosPath {
  182.             #if !PLATFORM_UNIX
  183.             get { return (m_Flags & (Flags.UncPath | Flags.DosPath)) != 0; }
  184.             #else
  185.             set { return false; }
  186.         }
  187.         #endif // !PLATFORM_UNIX
  188.        
  189.         private bool IsDosPath {
  190.             #if !PLATFORM_UNIX
  191.             get { return (m_Flags & Flags.DosPath) != 0; }
  192.             #else
  193.             set { return false; }
  194.         }
  195.         #endif // !PLATFORM_UNIX
  196.        
  197.         private bool IsUncPath {
  198.             #if !PLATFORM_UNIX
  199.             get { return (m_Flags & Flags.UncPath) != 0; }
  200.             #else
  201.             set { return false; }
  202.         }
  203.         #endif // !PLATFORM_UNIX
  204.        
  205.         private Flags HostType {
  206.             get { return m_Flags & Flags.HostTypeMask; }
  207.         }
  208.        
  209.         private UriParser Syntax {
  210.             get { return m_Syntax; }
  211.         }
  212.        
  213.         private bool IsNotAbsoluteUri {
  214.             get { return (object)m_Syntax == null; }
  215.         }
  216.        
  217.         internal bool UserDrivenParsing {
  218.             get { return (m_Flags & Flags.UserDrivenParsing) != 0; }
  219.         }
  220.         private void SetUserDrivenParsing()
  221.         {
  222.             // we use = here to clear all parsing flags for a uri that we think is invalid.
  223.             m_Flags = Flags.UserDrivenParsing | (m_Flags & Flags.UserEscaped);
  224.         }
  225.        
  226.         private ushort SecuredPathIndex {
  227.             get {
  228.                 // This is one more trouble with a Dos Path.
  229.                 // This property gets "safe" first path slash that is not the first if path = c:\
  230.                 if (IsDosPath) {
  231.                     char ch = m_String[m_Info.Offset.Path];
  232.                     return (ushort)((ch == '/' || ch == '\\') ? 3 : 2);
  233.                 }
  234.                 return (ushort)0;
  235.             }
  236.         }
  237.        
  238.         private bool NotAny(Flags flags)
  239.         {
  240.             return (m_Flags & flags) == 0;
  241.         }
  242.        
  243.         private bool InFact(Flags flags)
  244.         {
  245.             return (m_Flags & flags) != 0;
  246.         }
  247.        
  248.         private static bool StaticNotAny(Flags allFlags, Flags checkFlags)
  249.         {
  250.             return (allFlags & checkFlags) == 0;
  251.         }
  252.        
  253.         private static bool StaticInFact(Flags allFlags, Flags checkFlags)
  254.         {
  255.             return (allFlags & checkFlags) != 0;
  256.         }
  257.        
  258.         //
  259.         //
  260.         private UriInfo EnsureUriInfo()
  261.         {
  262.             Flags cF = m_Flags;
  263.             if (NotAny(Flags.MinimalUriInfoSet)) {
  264.                 CreateUriInfo(cF);
  265.             }
  266.             return m_Info;
  267.         }
  268.         //
  269.         //
  270.         private void EnsureParseRemaining()
  271.         {
  272.             if (NotAny(Flags.AllUriInfoSet)) {
  273.                 ParseRemaining();
  274.             }
  275.         }
  276.         //
  277.         //
  278.         private void EnsureHostString(bool allowDnsOptimization)
  279.         {
  280.             EnsureUriInfo();
  281.             if ((object)m_Info.Host == null) {
  282.                 if (allowDnsOptimization && InFact(Flags.CanonicalDnsHost)) {
  283.                     /* Optimization for a canonical DNS name
  284.                     *  ATTN: the host string won't be created,
  285.                     *  Hence ALL m_Info.Host callers first call EnsureHostString(false)
  286.                     *  For example IsLoopBack property is one of such callers.
  287.                     */                   
  288. return;
  289.                 }
  290.                 CreateHostString();
  291.             }
  292.         }
  293.        
  294.         //
  295.         // Uri(string)
  296.         //
  297.         // We expect to create a Uri from a display name - e.g. that was typed by
  298.         // a user, or that was copied & pasted from a document. That is, we do not
  299.         // expect already encoded URI to be supplied.
  300.         //
  301.         public Uri(string uriString)
  302.         {
  303.             if ((object)uriString == null)
  304.                 throw new ArgumentNullException("uriString");
  305.            
  306.             CreateThis(uriString, false, UriKind.Absolute);
  307.         }
  308.        
  309.        
  310.         //
  311.         // Uri(string, bool)
  312.         //
  313.         // Uri constructor. Assumes that input string is canonically escaped
  314.         //
  315.         [Obsolete("The constructor has been deprecated. Please use new Uri(string). The dontEscape parameter is deprecated and is always false. http://go.microsoft.com/fwlink/?linkid=14202")]
  316.         public Uri(string uriString, bool dontEscape)
  317.         {
  318.             if ((object)uriString == null)
  319.                 throw new ArgumentNullException("uriString");
  320.            
  321.             CreateThis(uriString, dontEscape, UriKind.Absolute);
  322.         }
  323.        
  324.        
  325.         //
  326.         // Uri(string, UriKind);
  327.         //
  328.         public Uri(string uriString, UriKind uriKind)
  329.         {
  330.             if ((object)uriString == null)
  331.                 throw new ArgumentNullException("uriString");
  332.            
  333.             CreateThis(uriString, false, uriKind);
  334.         }
  335.        
  336.        
  337.         //
  338.         // Uri(Uri, string)
  339.         //
  340.         // Construct a new Uri from a base and relative URI. The relative URI may
  341.         // also be an absolute URI, in which case the resultant URI is constructed
  342.         // entirely from it
  343.         //
  344.         public Uri(Uri baseUri, string relativeUri)
  345.         {
  346.             if ((object)baseUri == null)
  347.                 throw new ArgumentNullException("baseUri");
  348.            
  349.             if (!baseUri.IsAbsoluteUri)
  350.                 throw new ArgumentOutOfRangeException("baseUri");
  351.            
  352.             CreateUri(baseUri, relativeUri, false);
  353.         }
  354.        
  355.         //
  356.         // Uri(Uri, string, bool)
  357.         //
  358.         // Uri combinatorial constructor. Do not perform character escaping if
  359.         // DontEscape is true
  360.         //
  361.         [Obsolete("The constructor has been deprecated. Please new Uri(Uri, string). The dontEscape parameter is deprecated and is always false. http://go.microsoft.com/fwlink/?linkid=14202")]
  362.         public Uri(Uri baseUri, string relativeUri, bool dontEscape)
  363.         {
  364.             if ((object)baseUri == null)
  365.                 throw new ArgumentNullException("baseUri");
  366.            
  367.             if (!baseUri.IsAbsoluteUri)
  368.                 throw new ArgumentOutOfRangeException("baseUri");
  369.            
  370.             CreateUri(baseUri, relativeUri, dontEscape);
  371.         }
  372.        
  373.         private void CreateUri(Uri baseUri, string relativeUri, bool dontEscape)
  374.         {
  375.             // Parse relativeUri and populate Uri internal data.
  376.             CreateThis(relativeUri, dontEscape, UriKind.RelativeOrAbsolute);
  377.            
  378.             UriFormatException e;
  379.             if (baseUri.Syntax.IsSimple) {
  380.                 // Resolve Uris if possible OR get merged Uri String to re-parse below
  381.                 Uri uriResult = ResolveHelper(baseUri, this, ref relativeUri, ref dontEscape, out e);
  382.                
  383.                 if (e != null)
  384.                     throw e;
  385.                
  386.                 // If resolved into a Uri then we build from that Uri
  387.                 if (uriResult != null) {
  388.                     if ((object)uriResult != (object)this)
  389.                         CreateThisFromUri(uriResult);
  390.                    
  391.                     return;
  392.                 }
  393.             }
  394.             else {
  395.                 dontEscape = false;
  396.                 relativeUri = baseUri.Syntax.InternalResolve(baseUri, this, out e);
  397.                 if (e != null)
  398.                     throw e;
  399.             }
  400.            
  401.             m_Flags = Flags.Zero;
  402.             m_Info = null;
  403.             m_Syntax = null;
  404.             // If not resolved, we reparse modified Uri string and populate Uri internal data.
  405.             CreateThis(relativeUri, dontEscape, UriKind.Absolute);
  406.         }
  407.        
  408.         //
  409.         // Uri(Uri , Uri )
  410.         // Note: a static Create() method should be used by users, not this .ctor
  411.         //
  412.         public Uri(Uri baseUri, Uri relativeUri)
  413.         {
  414.             if ((object)baseUri == null)
  415.                 throw new ArgumentNullException("baseUri");
  416.            
  417.             if (!baseUri.IsAbsoluteUri)
  418.                 throw new ArgumentOutOfRangeException("baseUri");
  419.            
  420.             CreateThisFromUri(relativeUri);
  421.            
  422.             string newUriString = null;
  423.             UriFormatException e;
  424.             bool dontEscape;
  425.            
  426.             if (baseUri.Syntax.IsSimple) {
  427.                 dontEscape = InFact(Flags.UserEscaped);
  428.                 relativeUri = ResolveHelper(baseUri, this, ref newUriString, ref dontEscape, out e);
  429.                
  430.                 if (e != null)
  431.                     throw e;
  432.                
  433.                 if (relativeUri != null) {
  434.                     if ((object)relativeUri != (object)this)
  435.                         CreateThisFromUri(relativeUri);
  436.                    
  437.                     return;
  438.                 }
  439.             }
  440.             else {
  441.                 dontEscape = false;
  442.                 newUriString = baseUri.Syntax.InternalResolve(baseUri, this, out e);
  443.                 if (e != null)
  444.                     throw e;
  445.             }
  446.            
  447.             m_Flags = Flags.Zero;
  448.             m_Info = null;
  449.             m_Syntax = null;
  450.             CreateThis(newUriString, dontEscape, UriKind.Absolute);
  451.         }
  452.        
  453.         //
  454.         // ISerializable constructor
  455.         //
  456.         protected Uri(SerializationInfo serializationInfo, StreamingContext streamingContext)
  457.         {
  458.             string uriString = serializationInfo.GetString("AbsoluteUri");
  459.            
  460.             if (uriString.Length != 0) {
  461.                 CreateThis(uriString, false, UriKind.Absolute);
  462.                 return;
  463.             }
  464.            
  465.             uriString = serializationInfo.GetString("RelativeUri");
  466.             if ((object)uriString == null)
  467.                 throw new ArgumentNullException("uriString");
  468.            
  469.             CreateThis(uriString, false, UriKind.Relative);
  470.         }
  471.         //
  472.         // This method is shared by base+relative Uris constructors and is only called from them.
  473.         // The assumptions:
  474.         // - baseUri is a valid absolute Uri
  475.         // - relative part is not null and not empty
  476.         unsafe private static ParsingError GetCombinedString(Uri baseUri, string relativeStr, bool dontEscape, ref string result)
  477.         {
  478.             // NB: This is not RFC2396 compliant although it is inline with w3c.org recommendations
  479.             // This parser will allow the relativeStr to be an absolute Uri with the different scheme
  480.             // In fact this is strict violation of RFC2396
  481.             //
  482.             for (int i = 0; i < relativeStr.Length; ++i) {
  483.                 if (relativeStr[i] == '/' || relativeStr[i] == '\\' || relativeStr[i] == '?' || relativeStr[i] == '#') {
  484.                     break;
  485.                 }
  486.                 else if (relativeStr[i] == ':') {
  487.                     if (i < 2) {
  488.                         // Note we don't support one-letter Uri schemes.
  489.                         // Hence anything like x:sdsd is a relative path and be added to the baseUri Path
  490.                         break;
  491.                     }
  492.                     string scheme = relativeStr.Substring(0, i);
  493.                     fixed (char* sptr = scheme) {
  494.                         UriParser syntax = null;
  495.                         if (CheckSchemeSyntax(sptr, (ushort)scheme.Length, ref syntax) == ParsingError.None) {
  496.                             if (baseUri.Syntax == syntax) {
  497.                                 //Remove the scheme for backward Uri parsers compatibility
  498.                                 if (i + 1 < relativeStr.Length) {
  499.                                     relativeStr = relativeStr.Substring(i + 1);
  500.                                 }
  501.                                 else {
  502.                                     relativeStr = string.Empty;
  503.                                 }
  504.                             }
  505.                             else {
  506.                                 // This is the place where we switch the scheme.
  507.                                 // Return relative part as the result Uri.
  508.                                 result = relativeStr;
  509.                                 return ParsingError.None;
  510.                             }
  511.                         }
  512.                     }
  513.                     break;
  514.                 }
  515.             }
  516.            
  517.             if (relativeStr.Length == 0) {
  518.                 result = baseUri.OriginalString;
  519.                 return ParsingError.None;
  520.             }
  521.            
  522.             result = CombineUri(baseUri, relativeStr, dontEscape ? UriFormat.UriEscaped : UriFormat.SafeUnescaped);
  523.             return ParsingError.None;
  524.         }
  525.         //
  526.         private static UriFormatException GetException(ParsingError err)
  527.         {
  528.             switch (err) {
  529.                 case ParsingError.None:
  530.                     return null;
  531.                 case ParsingError.BadFormat:
  532.                     // Could be OK for Relative Uri
  533.                     return System.Net.ExceptionHelper.BadFormatException;
  534.                 case ParsingError.BadScheme:
  535.                     return System.Net.ExceptionHelper.BadSchemeException;
  536.                 case ParsingError.BadAuthority:
  537.                     return System.Net.ExceptionHelper.BadAuthorityException;
  538.                 case ParsingError.EmptyUriString:
  539.                     return System.Net.ExceptionHelper.EmptyUriException;
  540.                 case ParsingError.SchemeLimit:
  541.                     // Fatal
  542.                     return System.Net.ExceptionHelper.SchemeLimitException;
  543.                 case ParsingError.SizeLimit:
  544.                     return System.Net.ExceptionHelper.SizeLimitException;
  545.                 case ParsingError.MustRootedPath:
  546.                     return System.Net.ExceptionHelper.MustRootedPathException;
  547.                 case ParsingError.BadHostName:
  548.                     // Derived class controllable
  549.                     return System.Net.ExceptionHelper.BadHostNameException;
  550.                 case ParsingError.NonEmptyHost:
  551.                     return System.Net.ExceptionHelper.BadFormatException;
  552.                 case ParsingError.BadPort:
  553.                     //unix-only
  554.                     return System.Net.ExceptionHelper.BadPortException;
  555.                 case ParsingError.BadAuthorityTerminator:
  556.                     return System.Net.ExceptionHelper.BadAuthorityTerminatorException;
  557.                 case ParsingError.CannotCreateRelative:
  558.                     return System.Net.ExceptionHelper.CannotCreateRelativeException;
  559.                 default:
  560.                     break;
  561.             }
  562.             return System.Net.ExceptionHelper.BadFormatException;
  563.         }
  564.         //
  565.         // ISerializable method
  566.         //
  567.         /// <internalonly/>
  568.         [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
  569.         void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
  570.         {
  571.             GetObjectData(serializationInfo, streamingContext);
  572.         }
  573.        
  574.         //
  575.         // FxCop: provide some way for derived classes to access GetObjectData even if the derived class
  576.         // explicitly re-inherits ISerializable.
  577.         //
  578.         [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
  579.         protected void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
  580.         {
  581.            
  582.             if (IsAbsoluteUri)
  583.                 serializationInfo.AddValue("AbsoluteUri", GetParts(UriComponents.SerializationInfoString, UriFormat.UriEscaped));
  584.             else {
  585.                 serializationInfo.AddValue("AbsoluteUri", string.Empty);
  586.                 serializationInfo.AddValue("RelativeUri", GetParts(UriComponents.SerializationInfoString, UriFormat.UriEscaped));
  587.             }
  588.         }
  589.        
  590.         //
  591.         //
  592.         //
  593.         public string AbsolutePath {
  594.             get {
  595.                 if (IsNotAbsoluteUri) {
  596.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  597.                 }
  598.                
  599.                 string path = PrivateAbsolutePath;
  600.                 if (IsDosPath && path[0] == '/') {
  601.                     path = path.Substring(1);
  602.                 }
  603.                 return path;
  604.             }
  605.         }
  606.         //
  607.         private string PrivateAbsolutePath {
  608.             get {
  609.                 UriInfo info = EnsureUriInfo();
  610.                 if ((object)info.MoreInfo == null) {
  611.                     info.MoreInfo = new MoreInfo();
  612.                 }
  613.                 string result = info.MoreInfo.Path;
  614.                 if ((object)result == null) {
  615.                     result = GetParts(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.UriEscaped);
  616.                     info.MoreInfo.Path = result;
  617.                 }
  618.                 return result;
  619.             }
  620.         }
  621.         //
  622.         //
  623.         //
  624.         public string AbsoluteUri {
  625.             get {
  626.                 if (IsNotAbsoluteUri) {
  627.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  628.                 }
  629.                
  630.                 UriInfo info = EnsureUriInfo();
  631.                 if ((object)info.MoreInfo == null) {
  632.                     info.MoreInfo = new MoreInfo();
  633.                 }
  634.                 string result = info.MoreInfo.AbsoluteUri;
  635.                 if ((object)result == null) {
  636.                     result = GetParts(UriComponents.AbsoluteUri, UriFormat.UriEscaped);
  637.                     info.MoreInfo.AbsoluteUri = result;
  638.                 }
  639.                 return result;
  640.             }
  641.         }
  642.         //
  643.         //
  644.         // The result is of the form "hostname[:port]" Port is omitted if default
  645.         //
  646.         public string Authority {
  647.             get {
  648.                 if (IsNotAbsoluteUri) {
  649.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  650.                 }
  651.                
  652.                 return GetParts(UriComponents.Host | UriComponents.Port, UriFormat.UriEscaped);
  653.             }
  654.         }
  655.         //
  656.         //
  657.         //Gets a hostname part (special formatting for IPv6 form)
  658.         public string Host {
  659.             get {
  660.                 if (IsNotAbsoluteUri) {
  661.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  662.                 }
  663.                
  664.                 return GetParts(UriComponents.Host, UriFormat.UriEscaped);
  665.             }
  666.         }
  667.         //
  668.         //
  669.         public UriHostNameType HostNameType {
  670.             get {
  671.                 if (IsNotAbsoluteUri) {
  672.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  673.                 }
  674.                
  675.                 if (m_Syntax.IsSimple)
  676.                     EnsureUriInfo();
  677.                 else {
  678.                     // For a custom parser we request HostString creation to confirm HostType
  679.                     EnsureHostString(false);
  680.                 }
  681.                
  682.                 switch (HostType) {
  683.                     case Flags.DnsHostType:
  684.                         return UriHostNameType.Dns;
  685.                     case Flags.IPv4HostType:
  686.                         return UriHostNameType.IPv4;
  687.                     case Flags.IPv6HostType:
  688.                         return UriHostNameType.IPv6;
  689.                     case Flags.BasicHostType:
  690.                         return UriHostNameType.Basic;
  691.                     case Flags.UncHostType:
  692.                         #if !PLATFORM_UNIX
  693.                         return UriHostNameType.Basic;
  694.                     case Flags.UnknownHostType:
  695.                         //return (UriHostNameType)(UriHostNameType.Basic+10);
  696.                         #endif // !PLATFORM_UNIX
  697.                         return UriHostNameType.Unknown;
  698.                     default:
  699.                         break;
  700.                 }
  701.                 return UriHostNameType.Unknown;
  702.             }
  703.         }
  704.         //
  705.         //
  706.         public bool IsDefaultPort {
  707.             get {
  708.                 if (IsNotAbsoluteUri) {
  709.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  710.                 }
  711.                 if (m_Syntax.IsSimple)
  712.                     EnsureUriInfo();
  713.                 else {
  714.                     // For a custom parser we request HostString creation that will aso set the port
  715.                     EnsureHostString(false);
  716.                 }
  717.                
  718.                 return NotAny(Flags.NotDefaultPort);
  719.             }
  720.         }
  721.         //
  722.         //
  723.         public bool IsFile {
  724.             get {
  725.                 if (IsNotAbsoluteUri) {
  726.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  727.                 }
  728.                
  729.                 return (object)m_Syntax.SchemeName == (object)UriSchemeFile;
  730.             }
  731.         }
  732.         private static bool StaticIsFile(UriParser syntax)
  733.         {
  734.             return syntax.InFact(UriSyntaxFlags.FileLikeUri);
  735.         }
  736.         //
  737.         //
  738.         public bool IsLoopback {
  739.             get {
  740.                 if (IsNotAbsoluteUri) {
  741.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  742.                 }
  743.                
  744.                 EnsureHostString(false);
  745.                
  746.                 return InFact(Flags.LoopbackHost);
  747.             }
  748.         }
  749.         //
  750.         //
  751.         public bool IsUnc {
  752.             get {
  753.                 if (IsNotAbsoluteUri) {
  754.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  755.                 }
  756.                 return IsUncPath;
  757.             }
  758.         }
  759.         //
  760.         // LocalPath
  761.         //
  762.         // Returns a 'local' version of the path. This is mainly for file: URI
  763.         // such that DOS and UNC paths are returned with '/' converted back to
  764.         // '\', and any escape sequences converted
  765.         //
  766.         // The form of the returned path is in NOT Escaped
  767.         //
  768.         public string LocalPath {
  769.             get {
  770.                 if (IsNotAbsoluteUri) {
  771.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  772.                 }
  773.                 return GetLocalPath();
  774.             }
  775.         }
  776.        
  777.         private string GetLocalPath()
  778.         {
  779.             EnsureParseRemaining();
  780.            
  781.             //Other cases will get a Unix-style path
  782.             if (IsUncOrDosPath) {
  783.                 EnsureHostString(false);
  784.                 int start;
  785.                
  786.                 // Do we have a valid local path right in m_string?
  787.                 if (NotAny(Flags.HostNotCanonical | Flags.PathNotCanonical | Flags.ShouldBeCompressed)) {
  788.                    
  789.                     start = IsUncPath ? m_Info.Offset.Host - 2 : m_Info.Offset.Path;
  790.                    
  791.                    
  792.                     string str = (IsImplicitFile && m_Info.Offset.Host == (IsDosPath ? 0 : 2) && m_Info.Offset.Query == m_Info.Offset.End) ? m_String : (IsDosPath && (m_String[start] == '/' || m_String[start] == '\\')) ? m_String.Substring(start + 1, m_Info.Offset.Query - start - 1) : m_String.Substring(start, m_Info.Offset.Query - start);
  793.                    
  794.                     // Should be a rare case, convert c|\ into c:\
  795.                     if (IsDosPath && str[1] == '|') {
  796.                         // Sadly, today there is no method for replacong just one occurrence
  797.                         str = str.Remove(1, 1);
  798.                         str = str.Insert(1, ":");
  799.                     }
  800.                    
  801.                     // check for all back slashes (though may be string.Replace is smart?)
  802.                     for (int i = 0; i < str.Length; ++i) {
  803.                         if (str[i] == '/') {
  804.                             str = str.Replace('/', '\\');
  805.                             break;
  806.                         }
  807.                     }
  808.                    
  809.                     return str;
  810.                 }
  811.                
  812.                 // Not everything went well, going hardcore
  813.                
  814.                 char[] result;
  815.                 int count = 0;
  816.                 start = m_Info.Offset.Path;
  817.                
  818.                 string host = m_Info.Host;
  819.                 result = new char[host.Length + 3 + m_Info.Offset.Fragment - m_Info.Offset.Path];
  820.                
  821.                 if (IsUncPath) {
  822.                     result[0] = '\\';
  823.                     result[1] = '\\';
  824.                     count = 2;
  825.                    
  826.                     UnescapeString(host, 0, host.Length, result, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly);
  827.                    
  828.                 }
  829.                 else {
  830.                     // Dos path
  831.                     if (m_String[start] == '/' || m_String[start] == '\\') {
  832.                         // Skip leading slash for a DOS path
  833.                         ++start;
  834.                     }
  835.                 }
  836.                
  837.                
  838.                 ushort pathStart = (ushort)count;
  839.                 //save for optional Compress() call
  840.                 UnescapeMode mode = (InFact(Flags.PathNotCanonical) && !IsImplicitFile) ? (UnescapeMode.Unescape | UnescapeMode.UnescapeAll) : UnescapeMode.CopyOnly;
  841.                 UnescapeString(m_String, start, m_Info.Offset.Query, result, ref count, c_DummyChar, c_DummyChar, c_DummyChar, mode);
  842.                
  843.                 // Possibly convert c|\ into c:\
  844.                 if (result[1] == '|')
  845.                     result[1] = ':';
  846.                
  847.                 if (InFact(Flags.ShouldBeCompressed)) {
  848.                     // suspecting not compressed path
  849.                     // For a dos path we won't compress the "x:" part if found /../ sequences
  850.                     result = Compress(result, (ushort)(IsDosPath ? pathStart + 2 : pathStart), ref count, m_Syntax);
  851.                 }
  852.                
  853.                 // We don't know whether all slashes were the back ones
  854.                 // Plus going through Compress will turn them into / anyway
  855.                 // Converting / back into \
  856.                 for (ushort i = 0; i < (ushort)count; ++i) {
  857.                     if (result[i] == '/') {
  858.                         result[i] = '\\';
  859.                     }
  860.                 }
  861.                
  862.                 return new string(result, 0, count);
  863.                
  864.             }
  865.             else {
  866.                 // Return unescaped canonical path
  867.                 // Note we cannot call GetParts here because it has circular dependancy on GelLocalPath method
  868.                 return GetUnescapedParts(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped);
  869.             }
  870.         }
  871.        
  872.         //
  873.         //
  874.         // Gets the escaped Uri.AbsolutePath and Uri.Query
  875.         // properties separated by a "?" character.
  876.         public string PathAndQuery {
  877.             get {
  878.                 if (IsNotAbsoluteUri) {
  879.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  880.                 }
  881.                
  882.                 string result = GetParts(UriComponents.PathAndQuery, UriFormat.UriEscaped);
  883.                 if (IsDosPath && result[0] == '/') {
  884.                     result = result.Substring(1);
  885.                 }
  886.                 return result;
  887.             }
  888.         }
  889.         //
  890.         //
  891.         //
  892.         //
  893.         public int Port {
  894.             get {
  895.                 if (IsNotAbsoluteUri) {
  896.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  897.                 }
  898.                
  899.                 if (m_Syntax.IsSimple)
  900.                     EnsureUriInfo();
  901.                 else {
  902.                     // For a custom parser we request HostString creation that will aso set the port
  903.                     EnsureHostString(false);
  904.                 }
  905.                
  906.                 if (InFact(Flags.NotDefaultPort)) {
  907.                     return (int)m_Info.Offset.PortValue;
  908.                 }
  909.                 return m_Syntax.DefaultPort;
  910.             }
  911.         }
  912.         //
  913.         //
  914.         //
  915.         // Gets the escaped query.
  916.         public string Query {
  917.             get {
  918.                 if (IsNotAbsoluteUri) {
  919.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  920.                 }
  921.                
  922.                 UriInfo info = EnsureUriInfo();
  923.                 if ((object)info.MoreInfo == null) {
  924.                     info.MoreInfo = new MoreInfo();
  925.                 }
  926.                 string result = info.MoreInfo.Query;
  927.                 if ((object)result == null) {
  928.                     result = GetParts(UriComponents.Query | UriComponents.KeepDelimiter, UriFormat.UriEscaped);
  929.                     info.MoreInfo.Query = result;
  930.                 }
  931.                 return result;
  932.             }
  933.         }
  934.         //
  935.         //
  936.         //
  937.         // Gets the escaped fragment.
  938.         public string Fragment {
  939.             get {
  940.                 if (IsNotAbsoluteUri) {
  941.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  942.                 }
  943.                
  944.                 UriInfo info = EnsureUriInfo();
  945.                 if ((object)info.MoreInfo == null) {
  946.                     info.MoreInfo = new MoreInfo();
  947.                 }
  948.                 string result = info.MoreInfo.Fragment;
  949.                 if ((object)result == null) {
  950.                     result = GetParts(UriComponents.Fragment | UriComponents.KeepDelimiter, UriFormat.UriEscaped);
  951.                     info.MoreInfo.Fragment = result;
  952.                 }
  953.                 return result;
  954.             }
  955.         }
  956.        
  957.         //
  958.         // Gets the Scheme string of this Uri
  959.         //
  960.         //
  961.         public string Scheme {
  962.             get {
  963.                 if (IsNotAbsoluteUri) {
  964.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  965.                 }
  966.                
  967.                 return m_Syntax.SchemeName;
  968.             }
  969.         }
  970.        
  971.         //
  972.         // Gets the exact string passed by a user.
  973.         public string OriginalString {
  974.             get { return m_String; }
  975.         }
  976.        
  977.         //
  978.         // Gets the host string that is unescaped and if it's Ipv6 host,
  979.         // then the returned string is suitable for DNS lookup.
  980.         //
  981.         // For Ipv6 this will strip [] and add ScopeId if was found in the original string
  982.         public string DnsSafeHost {
  983.             get {
  984.                
  985.                 if (IsNotAbsoluteUri) {
  986.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  987.                 }
  988.                
  989.                 EnsureHostString(false);
  990.                
  991.                 // Special case, will include ScopeID and strip [] around IPv6
  992.                 // This will also unescape the host string
  993.                 string ret = m_Info.Host;
  994.                
  995.                 if (HostType == Flags.IPv6HostType) {
  996.                     ret = ret.Substring(1, ret.Length - 2);
  997.                     if ((object)m_Info.ScopeId != null) {
  998.                         ret += m_Info.ScopeId;
  999.                     }
  1000.                 }
  1001.                 // We do not unescape anything but Basic host
  1002.                 else if (HostType == Flags.BasicHostType && InFact(Flags.HostNotCanonical | Flags.E_HostNotCanonical)) {
  1003.                     char[] dest = new char[ret.Length];
  1004.                     int count = 0;
  1005.                     UnescapeString(ret, 0, ret.Length, dest, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly);
  1006.                     ret = new string(dest, 0, count);
  1007.                 }
  1008.                
  1009.                 return ret;
  1010.             }
  1011.         }
  1012.        
  1013.         //
  1014.         // Returns false if the string passed in the constructor cannot be parsed as
  1015.         // valid AbsoluteUri. This could be a relative Uri instead.
  1016.         //
  1017.         public bool IsAbsoluteUri {
  1018.             get { return m_Syntax != null; }
  1019.         }
  1020.        
  1021.         //
  1022.         //
  1023.         // Gets an array of the segments that make up a URI.
  1024.         public string[] Segments {
  1025.             get {
  1026.                 if (IsNotAbsoluteUri) {
  1027.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  1028.                 }
  1029.                
  1030.                
  1031.                 string[] segments = null;
  1032.                 // used to be a class cached result
  1033.                 if (segments == null) {
  1034.                    
  1035.                     string path = PrivateAbsolutePath;
  1036.                    
  1037.                     if (path.Length == 0) {
  1038.                         segments = new string[0];
  1039.                     }
  1040.                     else {
  1041.                         System.Collections.ArrayList pathSegments = new System.Collections.ArrayList();
  1042.                         int current = 0;
  1043.                         while (current < path.Length) {
  1044.                             int next = path.IndexOf('/', current);
  1045.                             if (next == -1) {
  1046.                                 next = path.Length - 1;
  1047.                             }
  1048.                             pathSegments.Add(path.Substring(current, (next - current) + 1));
  1049.                             current = next + 1;
  1050.                         }
  1051.                         segments = (string[])(pathSegments.ToArray(typeof(string)));
  1052.                     }
  1053.                 }
  1054.                 return segments;
  1055.             }
  1056.         }
  1057.         //
  1058.         //
  1059.         // Returns 'true' if the 'dontEscape' parameter was set to 'true ' when the Uri instance was created.
  1060.         public bool UserEscaped {
  1061.             get { return InFact(Flags.UserEscaped); }
  1062.         }
  1063.         //
  1064.         //
  1065.         // Gets the user name, password, and other user specific information associated
  1066.         // with the Uniform Resource Identifier (URI).
  1067.         public string UserInfo {
  1068.             get {
  1069.                 if (IsNotAbsoluteUri) {
  1070.                     throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  1071.                 }
  1072.                
  1073.                 return GetParts(UriComponents.UserInfo, UriFormat.UriEscaped);
  1074.             }
  1075.         }
  1076.        
  1077.         //
  1078.         // CheckHostName
  1079.         //
  1080.         // Determines whether a host name authority is a valid Host name according
  1081.         // to DNS naming rules
  1082.         //
  1083.         // Returns:
  1084.         // true if <name> is valid else false
  1085.         //
  1086.         // Throws:
  1087.         // Nothing
  1088.         //
  1089.         public static UriHostNameType CheckHostName(string name)
  1090.         {
  1091.            
  1092.             if ((object)name == null || name.Length == 0 || name.Length > short.MaxValue) {
  1093.                 return UriHostNameType.Unknown;
  1094.             }
  1095.             int end = name.Length;
  1096.             unsafe {
  1097.                 fixed (char* fixedName = name) {
  1098.                    
  1099.                     if (name[0] == '[' && name[name.Length - 1] == ']') {
  1100.                         // we require that _entire_ name is recognized as ipv6 address
  1101.                         if (IPv6AddressHelper.IsValid(fixedName, 1, ref end) && end == name.Length) {
  1102.                             return UriHostNameType.IPv6;
  1103.                         }
  1104.                     }
  1105.                     end = name.Length;
  1106.                     if (IPv4AddressHelper.IsValid(fixedName, 0, ref end, false, false) && end == name.Length) {
  1107.                         return UriHostNameType.IPv4;
  1108.                     }
  1109.                     end = name.Length;
  1110.                     bool dummyBool = false;
  1111.                     if (DomainNameHelper.IsValid(fixedName, 0, ref end, ref dummyBool, false) && end == name.Length) {
  1112.                         return UriHostNameType.Dns;
  1113.                     }
  1114.                 }
  1115.                
  1116.                 //This checks the form without []
  1117.                 end = name.Length + 2;
  1118.                 // we require that _entire_ name is recognized as ipv6 address
  1119.                 name = "[" + name + "]";
  1120.                 fixed (char* newFixedName = name) {
  1121.                     if (IPv6AddressHelper.IsValid(newFixedName, 1, ref end) && end == name.Length) {
  1122.                         return UriHostNameType.IPv6;
  1123.                     }
  1124.                 }
  1125.             }
  1126.             return UriHostNameType.Unknown;
  1127.         }
  1128.         //
  1129.         // CheckSchemeName
  1130.         //
  1131.         // Determines whether a string is a valid scheme name according to RFC 2396.
  1132.         // Syntax is:
  1133.         // scheme = alpha *(alpha | digit | '+' | '-' | '.')
  1134.         //
  1135.         public static bool CheckSchemeName(string schemeName)
  1136.         {
  1137.             if (((object)schemeName == null) || (schemeName.Length == 0) || !IsAsciiLetter(schemeName[0])) {
  1138.                 return false;
  1139.             }
  1140.             for (int i = schemeName.Length - 1; i > 0; --i) {
  1141.                 if (!(IsAsciiLetterOrDigit(schemeName[i]) || (schemeName[i] == '+') || (schemeName[i] == '-') || (schemeName[i] == '.'))) {
  1142.                     return false;
  1143.                 }
  1144.             }
  1145.             return true;
  1146.         }
  1147.         //
  1148.         // Returns:
  1149.         // Number in the range 0..15
  1150.         //
  1151.         // Throws:
  1152.         // ArgumentException
  1153.         //
  1154.         public static int FromHex(char digit)
  1155.         {
  1156.             if (((digit >= '0') && (digit <= '9')) || ((digit >= 'A') && (digit <= 'F')) || ((digit >= 'a') && (digit <= 'f'))) {
  1157.                 return (digit <= '9') ? ((int)digit - (int)'0') : (((digit <= 'F') ? ((int)digit - (int)'A') : ((int)digit - (int)'a')) + 10);
  1158.             }
  1159.             throw new ArgumentException("digit");
  1160.         }
  1161.         //
  1162.         // GetHashCode
  1163.         //
  1164.         // Overrides default function (in Object class)
  1165.         //
  1166.         //
  1167.         [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.Infrastructure)]
  1168.         public override int GetHashCode()
  1169.         {
  1170.             if (IsNotAbsoluteUri) {
  1171.                 return CalculateCaseInsensitiveHashCode(OriginalString);
  1172.             }
  1173.            
  1174.             UriInfo info = EnsureUriInfo();
  1175.             if ((object)info.MoreInfo == null) {
  1176.                 info.MoreInfo = new MoreInfo();
  1177.             }
  1178.             int tempHash = info.MoreInfo.Hash;
  1179.             if (tempHash == 0) {
  1180.                 string chkString = info.MoreInfo.RemoteUrl;
  1181.                 if ((object)chkString == null)
  1182.                     chkString = GetParts(UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped);
  1183.                 tempHash = CalculateCaseInsensitiveHashCode(chkString);
  1184.                 if (tempHash == 0) {
  1185.                     tempHash = 16777216;
  1186.                     //making it not zero still large enough to be maped to zero by a hashtable
  1187.                 }
  1188.                 info.MoreInfo.Hash = tempHash;
  1189.             }
  1190.             return tempHash;
  1191.         }
  1192.        
  1193.         //
  1194.         // ToString
  1195.         //
  1196.         // The better implementation would be just
  1197.         //
  1198.         private const UriFormat V1ToStringUnescape = (UriFormat)32767;
  1199.        
  1200.         [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.Infrastructure)]
  1201.         public override string ToString()
  1202.         {
  1203.             if (IsNotAbsoluteUri) {
  1204.                 return OriginalString;
  1205.             }
  1206.            
  1207.             EnsureUriInfo();
  1208.             if ((object)m_Info.String == null) {
  1209.                
  1210.                 // V1.1 compat unless #353711 is appoved, otheriwse it should be just a call into GetParts() as shown below
  1211.                 // m_Info.String = GetParts(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped);
  1212.                
  1213.                 if (Syntax.IsSimple)
  1214.                     m_Info.String = GetComponentsHelper(UriComponents.AbsoluteUri, V1ToStringUnescape);
  1215.                 else
  1216.                     m_Info.String = GetParts(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped);
  1217.                
  1218.             }
  1219.             return m_Info.String;
  1220.         }
  1221.        
  1222.         //
  1223.         //
  1224.         // A static shortcut to Uri.Equals
  1225.         //
  1226.         [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.Infrastructure)]
  1227.         public static bool operator ==(Uri uri1, Uri uri2)
  1228.         {
  1229.             if ((object)uri1 == (object)uri2) {
  1230.                 return true;
  1231.             }
  1232.             if ((object)uri1 == null || (object)uri2 == null) {
  1233.                 return false;
  1234.             }
  1235.             return uri2.Equals(uri1);
  1236.         }
  1237.        
  1238.         //
  1239.         //
  1240.         // A static shortcut to !Uri.Equals
  1241.         //
  1242.         [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.Infrastructure)]
  1243.         public static bool operator !=(Uri uri1, Uri uri2)
  1244.         {
  1245.             if ((object)uri1 == (object)uri2) {
  1246.                 return false;
  1247.             }
  1248.            
  1249.             if ((object)uri1 == null || (object)uri2 == null) {
  1250.                 return true;
  1251.             }
  1252.            
  1253.             return !uri2.Equals(uri1);
  1254.         }
  1255.        
  1256.        
  1257.        
  1258.         //
  1259.         // Equals
  1260.         //
  1261.         // Overrides default function (in Object class)
  1262.         //
  1263.         // Assumes:
  1264.         // <comparand> is an object of class Uri
  1265.         //
  1266.         // Returns:
  1267.         // true if objects have the same value, else false
  1268.         //
  1269.         // Throws:
  1270.         // Nothing
  1271.         //
  1272.         [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.Infrastructure)]
  1273.         public override bool Equals(object comparand)
  1274.         {
  1275.             if ((object)comparand == null) {
  1276.                 return false;
  1277.             }
  1278.            
  1279.             if ((object)this == (object)comparand) {
  1280.                 return true;
  1281.             }
  1282.            
  1283.             Uri obj = comparand as Uri;
  1284.            
  1285.             //
  1286.             // we allow comparisons of Uri and String objects only. If a string
  1287.             // is passed, convert to Uri. This is inefficient, but allows us to
  1288.             // canonicalize the comparand, making comparison possible
  1289.             //
  1290.             if ((object)obj == null) {
  1291.                 string s = comparand as string;
  1292.                
  1293.                 if ((object)s == null)
  1294.                     return false;
  1295.                
  1296.                 if (!TryCreate(s, UriKind.RelativeOrAbsolute, out obj))
  1297.                     return false;
  1298.             }
  1299.            
  1300.             // Since v1.0 two Uris are equal if everything but fragment and UserInfo does match
  1301.            
  1302.             // This check is for a case where we already fixed up the equal references
  1303.             if ((object)this.m_String == (object)obj.m_String) {
  1304.                 return true;
  1305.             }
  1306.            
  1307.             if (IsAbsoluteUri != obj.IsAbsoluteUri)
  1308.                 return false;
  1309.            
  1310.             if (IsNotAbsoluteUri)
  1311.                 return OriginalString.Equals(obj.OriginalString);
  1312.            
  1313.             if (NotAny(Flags.AllUriInfoSet) || obj.NotAny(Flags.AllUriInfoSet)) {
  1314.                 // Try raw compare for m_Strings as the last chance to keep the working set small
  1315.                 if (!IsUncOrDosPath) {
  1316.                     if (m_String.Length == obj.m_String.Length) {
  1317.                         unsafe {
  1318.                             // Try case sensitive compare on m_Strings
  1319.                             fixed (char* pMe = m_String) {
  1320.                                 fixed (char* pShe = obj.m_String) {
  1321.                                     // This will never go negative since m_String is checked to be a valid URI
  1322.                                     int i = (m_String.Length - 1);
  1323.                                     for (; i >= 0; --i) {
  1324.                                         if (*(pMe + i) != *(pShe + i)) {
  1325.                                             break;
  1326.                                         }
  1327.                                     }
  1328.                                     if (i == -1) {
  1329.                                         return true;
  1330.                                     }
  1331.                                 }
  1332.                             }
  1333.                         }
  1334.                     }
  1335.                 }
  1336.                 else if (String.Compare(m_String, obj.m_String, StringComparison.OrdinalIgnoreCase) == 0) {
  1337.                     return true;
  1338.                 }
  1339.             }
  1340.            
  1341.             // Note that equality test will bring the working set of both
  1342.             // objects up to creation of m_Info.MoreInfo member
  1343.             EnsureUriInfo();
  1344.             obj.EnsureUriInfo();
  1345.            
  1346.             if (!UserDrivenParsing && !obj.UserDrivenParsing && Syntax.IsSimple && obj.Syntax.IsSimple) {
  1347.                 // Optimization of canonical DNS names by avoiding host string creation.
  1348.                 // Note there could be explicit ports specified that would invalidate path offsets
  1349.                 if (InFact(Flags.CanonicalDnsHost) && obj.InFact(Flags.CanonicalDnsHost)) {
  1350.                     ushort i1 = m_Info.Offset.Host;
  1351.                     ushort end1 = m_Info.Offset.Path;
  1352.                    
  1353.                     ushort i2 = obj.m_Info.Offset.Host;
  1354.                     ushort end2 = obj.m_Info.Offset.Path;
  1355.                     string str = obj.m_String;
  1356.                     //Taking the shortest part
  1357.                     if (end1 - i1 > end2 - i2) {
  1358.                         end1 = (ushort)(i1 + end2 - i2);
  1359.                     }
  1360.                     // compare and break on ':' if found
  1361.                     while (i1 < end1) {
  1362.                         if (m_String[i1] != str[i2]) {
  1363.                             return false;
  1364.                         }
  1365.                         if (str[i2] == ':') {
  1366.                             // The other must have ':' too to have equal host
  1367.                             break;
  1368.                         }
  1369.                         ++i1;
  1370.                         ++i2;
  1371.                     }
  1372.                    
  1373.                     // The longest host must have ':' or be of the same size
  1374.                     if (i1 < m_Info.Offset.Path && m_String[i1] != ':') {
  1375.                         return false;
  1376.                     }
  1377.                     if (i2 < end2 && str[i2] != ':') {
  1378.                         return false;
  1379.                     }
  1380.                     //hosts are equal!
  1381.                 }
  1382.                 else {
  1383.                     EnsureHostString(false);
  1384.                     obj.EnsureHostString(false);
  1385.                     if (!m_Info.Host.Equals(obj.m_Info.Host)) {
  1386.                         return false;
  1387.                     }
  1388.                 }
  1389.                
  1390.                 if (Port != obj.Port) {
  1391.                     return false;
  1392.                 }
  1393.             }
  1394.            
  1395.            
  1396.             UriInfo meInfo = m_Info;
  1397.             UriInfo sheInfo = obj.m_Info;
  1398.             if ((object)meInfo.MoreInfo == null) {
  1399.                 meInfo.MoreInfo = new MoreInfo();
  1400.             }
  1401.             if ((object)sheInfo.MoreInfo == null) {
  1402.                 sheInfo.MoreInfo = new MoreInfo();
  1403.             }
  1404.            
  1405.             // NB: To avoid a race condition when creating MoreInfo field
  1406.             // "meInfo" and "sheInfo" shall remain as local copies.
  1407.             string me = meInfo.MoreInfo.RemoteUrl;
  1408.             if ((object)me == null) {
  1409.                 me = GetParts(UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped);
  1410.                 meInfo.MoreInfo.RemoteUrl = me;
  1411.             }
  1412.             string she = sheInfo.MoreInfo.RemoteUrl;
  1413.             if ((object)she == null) {
  1414.                 she = obj.GetParts(UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped);
  1415.                 sheInfo.MoreInfo.RemoteUrl = she;
  1416.             }
  1417.            
  1418.             if (!IsUncOrDosPath) {
  1419.                 if (me.Length != she.Length) {
  1420.                     return false;
  1421.                 }
  1422.                 unsafe {
  1423.                     // Try case sensitive compare on m_Strings
  1424.                     fixed (char* pMe = me) {
  1425.                         fixed (char* pShe = she) {
  1426.                             char* endMe = pMe + me.Length;
  1427.                             char* endShe = pShe + me.Length;
  1428.                             while (endMe != pMe) {
  1429.                                 if (*--endMe != *--endShe) {
  1430.                                     return false;
  1431.                                 }
  1432.                             }
  1433.                             return true;
  1434.                         }
  1435.                     }
  1436.                 }
  1437.             }
  1438.            
  1439.            
  1440.             // if IsUncOrDosPath is true then we ignore case in the path comparison
  1441.             // Get Unescaped form as most safe for the comparison
  1442.             // Fragment AND UserInfo are ignored
  1443.             //
  1444.             return (String.Compare(meInfo.MoreInfo.RemoteUrl, sheInfo.MoreInfo.RemoteUrl, IsUncOrDosPath ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0);
  1445.         }
  1446.         //
  1447.         // GetLeftPart
  1448.         //
  1449.         // Returns part of the URI based on the parameters:
  1450.         //
  1451.         // Inputs:
  1452.         // <argument> part
  1453.         // Which part of the URI to return
  1454.         //
  1455.         // Returns:
  1456.         // The requested substring
  1457.         //
  1458.         // Throws:
  1459.         // UriFormatException if URI type doesn't have host-port or authority parts
  1460.         //
  1461.         public string GetLeftPart(UriPartial part)
  1462.         {
  1463.             if (IsNotAbsoluteUri) {
  1464.                 throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  1465.             }
  1466.            
  1467.             EnsureUriInfo();
  1468.             const UriComponents NonPathPart = (UriComponents.Scheme | UriComponents.UserInfo | UriComponents.Host | UriComponents.Port);
  1469.            
  1470.             switch (part) {
  1471.                 case UriPartial.Scheme:
  1472.                    
  1473.                     return GetParts(UriComponents.Scheme | UriComponents.KeepDelimiter, UriFormat.UriEscaped);
  1474.                 case UriPartial.Authority:
  1475.                    
  1476.                    
  1477.                     if (NotAny(Flags.AuthorityFound) || IsDosPath) {
  1478.                        
  1479.                        
  1480.                        
  1481.                        
  1482.                         return String.Empty;
  1483.                     }
  1484.                     return GetParts(NonPathPart, UriFormat.UriEscaped);
  1485.                 case UriPartial.Path:
  1486.                    
  1487.                     return GetParts(NonPathPart | UriComponents.Path, UriFormat.UriEscaped);
  1488.                 case UriPartial.Query:
  1489.                    
  1490.                     return GetParts(NonPathPart | UriComponents.Path | UriComponents.Query, UriFormat.UriEscaped);
  1491.                
  1492.             }
  1493.             throw new ArgumentException("part");
  1494.         }
  1495.        
  1496.         //
  1497.         //
  1498.         /// Transforms a character into its hexadecimal representation.
  1499.         public static string HexEscape(char character)
  1500.         {
  1501.             if (character > 'ÿ') {
  1502.                 throw new ArgumentOutOfRangeException("character");
  1503.             }
  1504.             char[] chars = new char[3];
  1505.             int pos = 0;
  1506.             EscapeAsciiChar(character, chars, ref pos);
  1507.             return new string(chars);
  1508.         }
  1509.        
  1510.         //
  1511.         // HexUnescape
  1512.         //
  1513.         // Converts a substring of the form "%XX" to the single character represented
  1514.         // by the hexadecimal value XX. If the substring s[Index] does not conform to
  1515.         // the hex encoding format then the character at s[Index] is returned
  1516.         //
  1517.         // Inputs:
  1518.         // <argument> pattern
  1519.         // String from which to read the hexadecimal encoded substring
  1520.         //
  1521.         // <argument> index
  1522.         // Offset within <pattern> from which to start reading the hexadecimal
  1523.         // encoded substring
  1524.         //
  1525.         // Outputs:
  1526.         // <argument> index
  1527.         // Incremented to the next character position within the string. This
  1528.         // may be EOS if this was the last character/encoding within <pattern>
  1529.         //
  1530.         // Returns:
  1531.         // Either the converted character if <pattern>[<index>] was hex encoded, or
  1532.         // the character at <pattern>[<index>]
  1533.         //
  1534.         // Throws:
  1535.         // ArgumentOutOfRangeException
  1536.         //
  1537.        
  1538.         public static char HexUnescape(string pattern, ref int index)
  1539.         {
  1540.             if ((index < 0) || (index >= pattern.Length)) {
  1541.                 throw new ArgumentOutOfRangeException("index");
  1542.             }
  1543.             if ((pattern[index] == '%') && (pattern.Length - index >= 3)) {
  1544.                 char ret = EscapedAscii(pattern[index + 1], pattern[index + 2]);
  1545.                 if (ret != c_DummyChar) {
  1546.                     index += 3;
  1547.                     return ret;
  1548.                 }
  1549.             }
  1550.             return pattern[index++];
  1551.         }
  1552.        
  1553.         //
  1554.         // IsHexDigit
  1555.         //
  1556.         // Determines whether a character is a valid hexadecimal digit in the range
  1557.         // [0..9] | [A..F] | [a..f]
  1558.         //
  1559.         // Inputs:
  1560.         // <argument> character
  1561.         // Character to test
  1562.         //
  1563.         // Returns:
  1564.         // true if <character> is a hexadecimal digit character
  1565.         //
  1566.         // Throws:
  1567.         // Nothing
  1568.         //
  1569.         public static bool IsHexDigit(char character)
  1570.         {
  1571.             return ((character >= '0') && (character <= '9')) || ((character >= 'A') && (character <= 'F')) || ((character >= 'a') && (character <= 'f'));
  1572.         }
  1573.        
  1574.         //
  1575.         // IsHexEncoding
  1576.         //
  1577.         // Determines whether a substring has the URI hex encoding format of '%'
  1578.         // followed by 2 hexadecimal characters
  1579.         //
  1580.         // Inputs:
  1581.         // <argument> pattern
  1582.         // String to check
  1583.         //
  1584.         // <argument> index
  1585.         // Offset in <pattern> at which to check substring for hex encoding
  1586.         //
  1587.         // Assumes:
  1588.         // 0 <= <index> < <pattern>.Length
  1589.         //
  1590.         // Returns:
  1591.         // true if <pattern>[<index>] is hex encoded, else false
  1592.         //
  1593.         // Throws:
  1594.         // Nothing
  1595.         //
  1596.         public static bool IsHexEncoding(string pattern, int index)
  1597.         {
  1598.             if ((pattern.Length - index) < 3) {
  1599.                 return false;
  1600.             }
  1601.             if ((pattern[index] == '%') && EscapedAscii(pattern[index + 1], pattern[index + 1]) != c_DummyChar) {
  1602.                 return true;
  1603.             }
  1604.             return false;
  1605.         }
  1606.        
  1607.         //
  1608.         // MakeRelative (toUri)
  1609.         //
  1610.         // Return a relative path which when applied to this Uri would create the
  1611.         // resulting Uri <toUri>
  1612.         //
  1613.         // Inputs:
  1614.         // <argument> toUri
  1615.         // Uri to which we calculate the transformation from this Uri
  1616.         //
  1617.         // Returns:
  1618.         // If the 2 Uri are common except for a relative path difference, then that
  1619.         // difference, else the display name of this Uri
  1620.         //
  1621.         // Throws:
  1622.         // Nothing
  1623.         //
  1624.         [Obsolete("The method has been deprecated. Please use MakeRelativeUri(Uri uri). http://go.microsoft.com/fwlink/?linkid=14202")]
  1625.         public string MakeRelative(Uri toUri)
  1626.         {
  1627.             if (IsNotAbsoluteUri || toUri.IsNotAbsoluteUri)
  1628.                 throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  1629.            
  1630.             if ((Scheme == toUri.Scheme) && (Host == toUri.Host) && (Port == toUri.Port))
  1631.                 return PathDifference(AbsolutePath, toUri.AbsolutePath, !IsUncOrDosPath);
  1632.            
  1633.             return toUri.ToString();
  1634.         }
  1635.         //
  1636.         public Uri MakeRelativeUri(Uri uri)
  1637.         {
  1638.             if (IsNotAbsoluteUri || uri.IsNotAbsoluteUri)
  1639.                 throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
  1640.            
  1641.             // Note that the UserInfo part is ignored when computing a relative Uri.
  1642.             if ((Scheme == uri.Scheme) && (Host == uri.Host) && (Port == uri.Port))
  1643.                 return new Uri(PathDifference(AbsolutePath, uri.AbsolutePath, !IsUncOrDosPath) + uri.GetParts(UriComponents.Query | UriComponents.Fragment, UriFormat.UriEscaped), UriKind.Relative);
  1644.            
  1645.             return uri;
  1646.         }
  1647.        
  1648.         //
  1649.         // http://host/Path/Path/File?Query is the base of
  1650.         // - http://host/Path/Path/File/ ... (those "File" words may be different in semantic but anyway)
  1651.         // - http://host/Path/Path/#Fragment
  1652.         // - http://host/Path/Path/?Query
  1653.         // - http://host/Path/Path/MoreDir/ ...
  1654.         // - http://host/Path/Path/OtherFile?Query
  1655.         // - http://host/Path/Path/Fl
  1656.         // - http://host/Path/Path/
  1657.         //
  1658.         // It is not a base for
  1659.         // - http://host/Path/Path (that last "Path" is not considered as a directory)
  1660.         // - http://host/Path/Path?Query
  1661.         // - http://host/Path/Path#Fragment
  1662.         // - http://host/Path/Path2/
  1663.         // - http://host/Path/Path2/MoreDir
  1664.         // - http://host/Path/File
  1665.         //
  1666.         // ASSUMES that strings like http://host/Path/Path/MoreDir/../../ have been canonicalized before going to this method.
  1667.         // ASSUMES that back slashes already have been converted if applicable.
  1668.         //
  1669.         unsafe private static bool TestForSubPath(char* pMe, ushort meLength, char* pShe, ushort sheLength, bool ignoreCase)
  1670.         {
  1671.             ushort i = 0;
  1672.             char chMe;
  1673.             char chShe;
  1674.            
  1675.             bool AllSameBeforeSlash = true;
  1676.            
  1677.             for (; i < meLength && i < sheLength; ++i) {
  1678.                 chMe = *(pMe + i);
  1679.                 chShe = *(pShe + i);
  1680.                
  1681.                 if (chMe == '?' || chMe == '#') {
  1682.                     // survived so far and pMe does not have any more path segments
  1683.                     return true;
  1684.                 }
  1685.                
  1686.                 // If pMe terminates a path segment, so must pShe
  1687.                 if (chMe == '/') {
  1688.                     if (chShe != '/') {
  1689.                         // comparison has falied
  1690.                         return false;
  1691.                     }
  1692.                     // plus the segments must be the same
  1693.                     if (!AllSameBeforeSlash) {
  1694.                         // comparison has falied
  1695.                         return false;
  1696.                     }
  1697.                     //so far so good
  1698.                     AllSameBeforeSlash = true;
  1699.                     continue;
  1700.                 }
  1701.                
  1702.                 // if pShe terminates then pMe must not have any more path segments
  1703.                 if (chShe == '?' || chShe == '#') {
  1704.                     break;
  1705.                 }
  1706.                
  1707.                 if (!ignoreCase) {
  1708.                     if (chMe != chShe) {
  1709.                         AllSameBeforeSlash = false;
  1710.                     }
  1711.                 }
  1712.                 else {
  1713.                     if (Char.ToLower(chMe, CultureInfo.InvariantCulture) != Char.ToLower(chShe, CultureInfo.InvariantCulture)) {
  1714.                         AllSameBeforeSlash = false;
  1715.                     }
  1716.                 }
  1717.             }
  1718.            
  1719.             // If me is longer then it must not have any more path segments
  1720.             for (; i < meLength; ++i) {
  1721.                 if ((chMe = *(pMe + i)) == '?' || chMe == '#') {
  1722.                     return true;
  1723.                 }
  1724.                 if (chMe == '/') {
  1725.                     return false;
  1726.                 }
  1727.             }
  1728.             //survived by getting to the end of pMe
  1729.             return true;
  1730.         }
  1731.         unsafe static internal string InternalEscapeString(string rawString)
  1732.         {
  1733.             if ((object)rawString == null)
  1734.                 return String.Empty;
  1735.            
  1736.             int position = 0;
  1737.             char[] dest = EscapeString(rawString, 0, rawString.Length, null, ref position, true, '?', '#', '%');
  1738.             if ((object)dest == null)
  1739.                 return rawString;
  1740.            
  1741.             return new string(dest, 0, position);
  1742.         }
  1743.        
  1744.         //
  1745.         // This method is called first to figure out the scheme or a simple file path
  1746.         // Is called only at the .ctor time
  1747.         //
  1748.         unsafe private static ParsingError ParseScheme(string uriString, ref Flags flags, ref UriParser syntax)
  1749.         {
  1750.             if (uriString.Length == 0)
  1751.                 return ParsingError.EmptyUriString;
  1752.            
  1753.             // we don;t work with >= 64k Uris
  1754.             if (uriString.Length >= c_MaxUriBufferSize)
  1755.                 return ParsingError.SizeLimit;
  1756.            
  1757.             //STEP1: parse scheme, lookup this Uri Syntax or create one using UnknownV1SyntaxFlags uri syntax template
  1758.             fixed (char* pUriString = uriString) {
  1759.                 ParsingError err = ParsingError.None;
  1760.                 ushort idx = ParseSchemeCheckImplicitFile(pUriString, (ushort)uriString.Length, ref err, ref flags, ref syntax);
  1761.                
  1762.                 if (err != ParsingError.None)
  1763.                     return err;
  1764.                
  1765.                 flags |= (Flags)idx;
  1766.             }
  1767.             return ParsingError.None;
  1768.         }
  1769.        
  1770.         //
  1771.         // A wrapper for ParseMinimal() called from a user parser
  1772.         // It signals back that the call has been done
  1773.         // plus it communicates back a flag for an error if any
  1774.         //
  1775.         internal UriFormatException ParseMinimal()
  1776.         {
  1777.             ParsingError result = PrivateParseMinimal();
  1778.             if (result == ParsingError.None)
  1779.                 return null;
  1780.            
  1781.             // Means the we think the Uri is invalid, bu that can be later overriden by a user parser
  1782.             m_Flags |= Flags.ErrorOrParsingRecursion;
  1783.            
  1784.             return GetException(result);
  1785.         }
  1786.         //
  1787.         //
  1788.         // This method tries to parse the minimal information needed to certify the valifity
  1789.         // of a uri string
  1790.         //
  1791.         // scheme://userinfo@host:Port/Path?Query#Fragment
  1792.         //
  1793.         // The method must be called only at the .ctor time
  1794.         //
  1795.         // Returns ParsingError.None if the Uri syntax is valid, an error otheriwse
  1796.         //
  1797.         unsafe private ParsingError PrivateParseMinimal()
  1798.         {
  1799.             ushort idx = (ushort)(m_Flags & Flags.IndexMask);
  1800.             ushort length = (ushort)m_String.Length;
  1801.            
  1802.             // Means a custom UriParser did call "base" InitializeAndValidate()
  1803.             m_Flags &= ~(Flags.IndexMask | Flags.UserDrivenParsing);
  1804.            
  1805.             //STEP2: Parse up to the port
  1806.             fixed (char* pUriString = m_String) {
  1807.                 // Cut trailing spaces in m_String
  1808.                 if (length > idx && IsLWS(pUriString[length - 1])) {
  1809.                     --length;
  1810.                     while (length != idx && IsLWS(pUriString[--length]))
  1811.                         ;
  1812.                     ++length;
  1813.                 }
  1814.                
  1815.                 //
  1816.                 #if !PLATFORM_UNIX
  1817.                 if (m_Syntax.IsAllSet(UriSyntaxFlags.AllowEmptyHost | UriSyntaxFlags.AllowDOSPath) && NotAny(Flags.ImplicitFile) && (idx + 1 < length)) {
  1818.                    
  1819.                     char c;
  1820.                     ushort i = (ushort)idx;
  1821.                    
  1822.                     for (; i < length; ++i) {
  1823.                         if (!((c = pUriString[i]) == '\\' || c == '/'))
  1824.                             break;
  1825.                     }
  1826.                    
  1827.                     if (m_Syntax.InFact(UriSyntaxFlags.FileLikeUri) || i - idx <= 3) {
  1828.                         // if more than one slash after the scheme, the authority is present
  1829.                         if (i - idx >= 2) {
  1830.                             m_Flags |= Flags.AuthorityFound;
  1831.                         }
  1832.                         // DOS-like path?
  1833.                         if (i + 1 < (ushort)length && ((c = pUriString[i + 1]) == ':' || c == '|') && IsAsciiLetter(pUriString[i])) {
  1834.                            
  1835.                             if (i + 2 >= (ushort)length || ((c = pUriString[i + 2]) != '\\' && c != '/')) {
  1836.                                 // report an error but only for a file: scheme
  1837.                                 if (m_Syntax.InFact(UriSyntaxFlags.FileLikeUri))
  1838.                                     return ParsingError.MustRootedPath;
  1839.                             }
  1840.                             else {
  1841.                                 // This will set IsDosPath
  1842.                                 m_Flags |= Flags.DosPath;
  1843.                                
  1844.                                 if (m_Syntax.InFact(UriSyntaxFlags.MustHaveAuthority)) {
  1845.                                     // when DosPath found and Authority is required, set this flag even if Authority is empty
  1846.                                     m_Flags |= Flags.AuthorityFound;
  1847.                                 }
  1848.                                 if (i != idx && i - idx != 2) {
  1849.                                     //This will remember that DosPath is rooted
  1850.                                     idx = (ushort)(i - 1);
  1851.                                 }
  1852.                                 else {
  1853.                                     idx = i;
  1854.                                 }
  1855.                             }
  1856.                         }
  1857.                         else if (m_Syntax.InFact(UriSyntaxFlags.FileLikeUri) && (i - idx >= 2 && i - idx != 3 && i < length && pUriString[i] != '?' && pUriString[i] != '#')) {
  1858.                             m_Flags |= Flags.UncPath;
  1859.                             idx = i;
  1860.                         }
  1861.                     }
  1862.                 }
  1863.                 #endif // !PLATFORM_UNIX
  1864.                 //
  1865.                 //STEP 1.5 decide on the Authority component
  1866.                 //
  1867.                 #if !PLATFORM_UNIX
  1868.                 if ((m_Flags & (Flags.UncPath | Flags.DosPath)) != 0) {
  1869.                 }
  1870.                 #else
  1871.                 if ((m_Flags & Flags.ImplicitFile) != 0) {
  1872.                     // Already parsed up to the path
  1873.                 }
  1874.                 #endif // !PLATFORM_UNIX
  1875.                 else if ((idx + 2) <= length) {
  1876.                     char first = pUriString[idx];
  1877.                     char second = pUriString[idx + 1];
  1878.                    
  1879.                     if (m_Syntax.InFact(UriSyntaxFlags.MustHaveAuthority)) {
  1880.                         // (V1.0 compatiblity) This will allow http:\\ http:\/ http:/\
  1881.                         #if !PLATFORM_UNIX
  1882.                         if ((first == '/' || first == '\\') && (second == '/' || second == '\\'))
  1883.                             #else
  1884.                             #endif // !PLATFORM_UNIX
  1885.                             if (first == '/' && second == '/') {
  1886.                                 m_Flags |= Flags.AuthorityFound;
  1887.                                 idx += 2;
  1888.                             }
  1889.                             else {
  1890.                                 return ParsingError.BadAuthority;
  1891.                             }
  1892.                     }
  1893.                     else if (m_Syntax.InFact(UriSyntaxFlags.OptionalAuthority) && (InFact(Flags.AuthorityFound) || (first == '/' && second == '/'))) {
  1894.                         m_Flags |= Flags.AuthorityFound;
  1895.                         idx += 2;
  1896.                     }
  1897.                     else if (m_Syntax.NotAny(UriSyntaxFlags.MailToLikeUri)) {
  1898.                         m_Flags |= ((Flags)idx | Flags.UnknownHostType);
  1899.                         return ParsingError.None;
  1900.                     }
  1901.                 }
  1902.                 else if (m_Syntax.InFact(UriSyntaxFlags.MustHaveAuthority)) {
  1903.                     return ParsingError.BadAuthority;
  1904.                 }
  1905.                 else if (m_Syntax.NotAny(UriSyntaxFlags.MailToLikeUri)) {
  1906.                     m_Flags |= ((Flags)idx | Flags.UnknownHostType);
  1907.                     return ParsingError.None;
  1908.                 }
  1909.                
  1910.                 #if !PLATFORM_UNIX
  1911.                 // The following sample taken from the original parser comments makes the whole story sad
  1912.                 // vsmacros://c:\path\file
  1913.                 // Note that two slashes say there must be an Authority but instead the path goes
  1914.                 // Fro V1 compat the next block allow this case but not for schemes like http
  1915.                 if (InFact(Flags.DosPath)) {
  1916.                    
  1917.                     m_Flags |= (((m_Flags & Flags.AuthorityFound) != 0) ? Flags.BasicHostType : Flags.UnknownHostType);
  1918.                     m_Flags |= (Flags)idx;
  1919.                     return ParsingError.None;
  1920.                 }
  1921.                 #endif // !PLATFORM_UNIX
  1922.                
  1923.                 //STEP 2: Check the syntax of authority expecting at least one character in it
  1924.                 //
  1925.                 // Note here we do know that there is an authority in the string OR it's a DOS path
  1926.                
  1927.                 // We may find a userInfo and the port when parsing an authority
  1928.                 // Also we may find a registry based authority.
  1929.                 // We must ensure that known schemes do use a server-based authority
  1930.                 {
  1931.                     ParsingError err = ParsingError.None;
  1932.                     idx = CheckAuthorityHelper(pUriString, idx, (ushort)length, ref err, ref m_Flags, m_Syntax);
  1933.                     if (err != ParsingError.None)
  1934.                         return err;
  1935.                    
  1936.                     // This will disallow '\' as the host terminator for any scheme that is not implicitFile or cannot have a Dos Path
  1937.                     if ((idx < (ushort)length && pUriString[idx] == '\\') && NotAny(Flags.ImplicitFile) && m_Syntax.NotAny(UriSyntaxFlags.AllowDOSPath))
  1938.                         return ParsingError.BadAuthorityTerminator;
  1939.                    
  1940.                 }
  1941.                
  1942.                 // The Path (or Port) parsing index is reloaded on demand in CreateUriInfo when accessing a Uri property
  1943.                 m_Flags |= (Flags)idx;
  1944.                
  1945.                 // The rest of the string will be parsed on demand
  1946.                 // The Host/Authorty is all checked, the type is known but the host value string
  1947.                 // is not created/canonicalized at this point.
  1948.             }
  1949.             return ParsingError.None;
  1950.         }
  1951.        
  1952.         //
  1953.         //
  1954.         // The method is called when we have to access m_Info members
  1955.         // This will create the m_Info based on the copied parser context
  1956.         // Under milti-threading race this method may do duplicated yet harmless work
  1957.         //
  1958.         private void CreateUriInfo(Flags cF)
  1959.         {
  1960.            
  1961.             UriInfo info = new UriInfo();
  1962.             // This will be revisited in ParseRemaining but for now just have it at least m_String.Length
  1963.             info.Offset.End = (ushort)m_String.Length;
  1964.            
  1965.             if (UserDrivenParsing)
  1966.                 goto Done;
  1967.            
  1968.             ushort idx;
  1969.             bool notCanonicalScheme = false;
  1970.            
  1971.             // The m_String may have leading spaces, figure that out
  1972.             // plus it will set idx value for next steps
  1973.             if (StaticInFact(cF, Flags.ImplicitFile)) {
  1974.                 idx = (ushort)0;
  1975.                 while (IsLWS(m_String[idx])) {
  1976.                     ++idx;
  1977.                     ++info.Offset.Scheme;
  1978.                 }
  1979.                
  1980.                 #if !PLATFORM_UNIX
  1981.                 if (StaticInFact(cF, Flags.UncPath)) {
  1982.                     // For implicit file AND Unc only
  1983.                     idx += 2;
  1984.                     //skip any other slashes (compatibility with V1.0 parser)
  1985.                     while (idx < (ushort)(cF & Flags.IndexMask) && (m_String[idx] == '/' || m_String[idx] == '\\')) {
  1986.                         ++idx;
  1987.                     }
  1988.                 }
  1989.                 #endif // !PLATFORM_UNIX
  1990.             }
  1991.             else {
  1992.                 // This is NOT an ImplicitFile uri
  1993.                 idx = (ushort)m_Syntax.SchemeName.Length;
  1994.                
  1995.                 while (m_String[idx++] != ':') {
  1996.                     ++info.Offset.Scheme;
  1997.                 }
  1998.                
  1999.                 if (StaticInFact(cF, Flags.AuthorityFound)) {
  2000.                     if (m_String[idx] == '\\' || m_String[idx + 1] == '\\')
  2001.                         notCanonicalScheme = true;
  2002.                    
  2003.                     idx += 2;
  2004.                     #if !PLATFORM_UNIX
  2005.                     if (StaticInFact(cF, Flags.UncPath | Flags.DosPath)) {
  2006.                         // Skip slashes if it was allowed during ctor time
  2007.                         // NB: Today this is only allowed if a Unc or DosPath was found after the scheme
  2008.                         while (idx < (ushort)(cF & Flags.IndexMask) && (m_String[idx] == '/' || m_String[idx] == '\\')) {
  2009.                             notCanonicalScheme = true;
  2010.                             ++idx;
  2011.                         }
  2012.                     }
  2013.                     #endif // !PLATFORM_UNIX
  2014.                 }
  2015.             }
  2016.            
  2017.             // This is weird but some schemes (mailto) do not have Authority-based syntax, still they do have a port
  2018.             if (m_Syntax.DefaultPort != UriParser.NoDefaultPort)
  2019.                 info.Offset.PortValue = (ushort)m_Syntax.DefaultPort;
  2020.            
  2021.             //Here we set the indexes for already parsed components
  2022.             #if !PLATFORM_UNIX
  2023.             #endif // !PLATFORM_UNIX
  2024.             if ((cF & Flags.HostTypeMask) == Flags.UnknownHostType || StaticInFact(cF, Flags.DosPath)) {
  2025.                 //there is no Authotity component defined
  2026.                 info.Offset.User = (ushort)(cF & Flags.IndexMask);
  2027.                 info.Offset.Host = info.Offset.User;
  2028.                 info.Offset.Path = info.Offset.User;
  2029.                 cF &= ~Flags.IndexMask;
  2030.                 if (notCanonicalScheme) {
  2031.                     cF |= Flags.SchemeNotCanonical;
  2032.                 }
  2033.                 goto Done;
  2034.             }
  2035.            
  2036.             info.Offset.User = idx;
  2037.            
  2038.             //Basic Host Type does not have userinfo and port
  2039.             if (HostType == Flags.BasicHostType) {
  2040.                 info.Offset.Host = idx;
  2041.                 info.Offset.Path = (ushort)(cF & Flags.IndexMask);
  2042.                 cF &= ~Flags.IndexMask;
  2043.                 goto Done;
  2044.             }
  2045.            
  2046.             if (StaticInFact(cF, Flags.HasUserInfo)) {
  2047.                 // we previously found a userinfo, get it again
  2048.                 while (m_String[idx] != '@') {
  2049.                     ++idx;
  2050.                 }
  2051.                 ++idx;
  2052.                 info.Offset.Host = idx;
  2053.             }
  2054.             else {
  2055.                 info.Offset.Host = idx;
  2056.             }
  2057.            
  2058.             //Now reload the end of the parsed host
  2059.             idx = (ushort)(cF & Flags.IndexMask);
  2060.            
  2061.             //From now on we do not need IndexMask bits, and reuse the space for X_NotCanonical flags
  2062.             //clear them now
  2063.             cF &= ~Flags.IndexMask;
  2064.            
  2065.             // If this is not canonical, don't count on user input to be good
  2066.             if (notCanonicalScheme) {
  2067.                 cF |= Flags.SchemeNotCanonical;
  2068.             }
  2069.            
  2070.             //Guessing this is a path start
  2071.             info.Offset.Path = idx;
  2072.            
  2073.             // parse Port if any. The new spec allows a port after ':' to be empty (assuming default?)
  2074.             bool notEmpty = false;
  2075.             // Note we already checked on general port syntax in ParseMinimal()
  2076.             if (idx < info.Offset.End && m_String[idx] == ':') {
  2077.                 int port = 0;
  2078.                
  2079.                 //Check on some noncanonical cases http://host:0324/, http://host:03, http://host:0, etc
  2080.                 if (++idx < info.Offset.End) {
  2081.                     port = (ushort)(m_String[idx] - '0');
  2082.                     if (!(port == unchecked((ushort)('/' - '0')) || port == (ushort)('?' - '0') || port == unchecked((ushort)('#' - '0')))) {
  2083.                         notEmpty = true;
  2084.                         if (port == 0) {
  2085.                             cF |= (Flags.PortNotCanonical | Flags.E_PortNotCanonical);
  2086.                         }
  2087.                         for (++idx; idx < info.Offset.End; ++idx) {
  2088.                             ushort val = (ushort)((ushort)m_String[idx] - (ushort)'0');
  2089.                             if (val == unchecked((ushort)('/' - '0')) || val == (ushort)('?' - '0') || val == unchecked((ushort)('#' - '0'))) {
  2090.                                 break;
  2091.                             }
  2092.                             port = (port * 10 + val);
  2093.                         }
  2094.                     }
  2095.                 }
  2096.                 if (notEmpty && info.Offset.PortValue != (ushort)port) {
  2097.                     info.Offset.PortValue = (ushort)port;
  2098.                     cF |= Flags.NotDefaultPort;
  2099.                 }
  2100.                 else {
  2101.                     //This will tell that we do have a ':' but the port value does
  2102.                     //not follow to canonical rules
  2103.                     cF |= (Flags.PortNotCanonical | Flags.E_PortNotCanonical);
  2104.                 }
  2105.                 info.Offset.Path = (ushort)idx;
  2106.             }
  2107.             Done:
  2108.            
  2109.             cF |= Flags.MinimalUriInfoSet;
  2110.             /*********
  2111.             //                                                                                                 
  2112.             int copyF = m_Flags;
  2113.             while ((copyF & Flags.MinimalUriInfoSet) == 0)
  2114.             {
  2115.               if (copyF != (copyF = Interlocked.CompareExchange(ref m_Flags, cF | (copyF & ~Flags.IndexMask), copyF))
  2116.                   continue;
  2117.               m_Info  = info;
  2118.             }
  2119. *********/           
  2120.            
  2121. lock (m_String) {
  2122.                 if (NotAny(Flags.MinimalUriInfoSet)) {
  2123.                     m_Info = info;
  2124.                     m_Flags = (m_Flags & ~Flags.IndexMask) | cF;
  2125.                 }
  2126.             }
  2127.            
  2128.         }
  2129.        
  2130.         //
  2131.         // This will create a Host string. The validity has been already checked
  2132.         //
  2133.         // Assuming: UriInfo memeber is already set at this point
  2134.         unsafe private void CreateHostString()
  2135.         {
  2136.             //
  2137.             // Mutlithrreading!
  2138.             //
  2139.             if (!m_Syntax.IsSimple) {
  2140.                 lock (m_Info) {
  2141.                     // ATTN: Avoid possible recursion through CreateHostString->Syntax.GetComponents->Uri.GetComponentsHelper->CreateHostString
  2142.                     if (NotAny(Flags.ErrorOrParsingRecursion)) {
  2143.                         m_Flags |= Flags.ErrorOrParsingRecursion;
  2144.                         // Need to get host string through the derived type
  2145.                         GetHostViaCustomSyntax();
  2146.                         m_Flags &= ~Flags.ErrorOrParsingRecursion;
  2147.                         return;
  2148.                     }
  2149.                 }
  2150.             }
  2151.             Flags flags = m_Flags;
  2152.             string host = CreateHostStringHelper(m_String, m_Info.Offset.Host, m_Info.Offset.Path, ref flags, ref m_Info.ScopeId);
  2153.            
  2154.             // now check on canonical host representation
  2155.             if (host.Length != 0) {
  2156.                 // An Authority may need escaping except when it's an inet server address
  2157.                 //
  2158.                 // We do not escape UNC names and will get rid of this type when switching to IDN spec
  2159.                 //
  2160.                 if (HostType == Flags.BasicHostType) {
  2161.                     ushort idx = 0;
  2162.                     Check result;
  2163.                     fixed (char* pHost = host) {
  2164.                         result = CheckCanonical(pHost, ref idx, (ushort)host.Length, c_DummyChar);
  2165.                     }
  2166.                    
  2167.                     if ((result & Check.DisplayCanonical) == 0) {
  2168.                         // For implicit file the user string must be in perfect display format,
  2169.                         // Hence, ignoring complains from CheckCanonical()
  2170.                         if (NotAny(Flags.ImplicitFile) || (result & Check.ReservedFound) != 0) {
  2171.                             flags |= Flags.HostNotCanonical;
  2172.                         }
  2173.                     }
  2174.                    
  2175.                     if (InFact(Flags.ImplicitFile) && (result & (Check.ReservedFound | Check.EscapedCanonical)) != 0) {
  2176.                         // need to re-escape this host if any escaped sequence was found
  2177.                         result &= ~Check.EscapedCanonical;
  2178.                     }
  2179.                    
  2180.                     if ((result & (Check.EscapedCanonical | Check.BackslashInPath)) != Check.EscapedCanonical) {
  2181.                         flags |= Flags.E_HostNotCanonical;
  2182.                         if (NotAny(Flags.UserEscaped)) {
  2183.                             int position = 0;
  2184.                             char[] dest = EscapeString(host, 0, host.Length, null, ref position, true, '?', '#', IsImplicitFile ? c_DummyChar : '%');
  2185.                             if ((object)dest != null)
  2186.                                 host = new string(dest, 0, position);
  2187.                         }
  2188.                         else {
  2189.                         }
  2190.                     }
  2191.                 }
  2192.                 else if (NotAny(Flags.CanonicalDnsHost)) {
  2193.                     // Check to see if we can take the canonical host string out of m_String
  2194.                     if ((object)m_Info.ScopeId != null) {
  2195.                         // IPv6 ScopeId is included when serializing a Uri
  2196.                         flags |= (Flags.HostNotCanonical | Flags.E_HostNotCanonical);
  2197.                     }
  2198.                     else
  2199.                         for (ushort i = 0; i < host.Length; ++i) {
  2200.                             if ((m_Info.Offset.Host + i) >= m_Info.Offset.End || host[i] != m_String[m_Info.Offset.Host + i]) {
  2201.                                 flags |= (Flags.HostNotCanonical | Flags.E_HostNotCanonical);
  2202.                                 break;
  2203.                             }
  2204.                         }
  2205.                 }
  2206.             }
  2207.            
  2208.             m_Info.Host = host;
  2209.             lock (m_Info) {
  2210.                 m_Flags |= flags;
  2211.             }
  2212.         }
  2213.         //
  2214.         private static string CreateHostStringHelper(string str, ushort idx, ushort end, ref Flags flags, ref string scopeId)
  2215.         {
  2216.             bool loopback = false;
  2217.             string host;
  2218.             switch (flags & Flags.HostTypeMask) {
  2219.                 case Flags.DnsHostType:
  2220.                    
  2221.                     host = DomainNameHelper.ParseCanonicalName(str, idx, end, ref loopback);
  2222.                     break;
  2223.                 case Flags.IPv6HostType:
  2224.                    
  2225.                     host = IPv6AddressHelper.ParseCanonicalName(str, idx, ref loopback, ref scopeId);
  2226.                     break;
  2227.                 case Flags.IPv4HostType:
  2228.                    
  2229.                     host = IPv4AddressHelper.ParseCanonicalName(str, idx, end, ref loopback);
  2230.                     break;
  2231.                 case Flags.UncHostType:
  2232.                    
  2233.                     #if !PLATFORM_UNIX
  2234.                     host = UncNameHelper.ParseCanonicalName(str, idx, end, ref loopback);
  2235.                     break;
  2236.                 case Flags.BasicHostType:
  2237.                     #endif // !PLATFORM_UNIX
  2238.                    
  2239.                     #if !PLATFORM_UNIX
  2240.                     if (StaticInFact(flags, Flags.DosPath)) {
  2241.                         host = string.Empty;
  2242.                     }
  2243.                     #endif // !PLATFORM_UNIX
  2244.                     else {
  2245.                         // This is for a registry-based authority, not relevant for known schemes
  2246.                         host = str.Substring(idx, end - idx);
  2247.                     }
  2248.                     // A empty host would count for a loopback
  2249.                     if (host.Length == 0) {
  2250.                         loopback = true;
  2251.                     }
  2252.                     //there will be no port
  2253.                     break;
  2254.                 case Flags.UnknownHostType:
  2255.                    
  2256.                     //means the host is *not expected* for this uri type
  2257.                     host = string.Empty;
  2258.                     break;
  2259.                 default:
  2260.                    
  2261.                     //it's a bug
  2262.                     throw GetException(ParsingError.BadHostName);
  2263.                     break;
  2264.             }
  2265.            
  2266.             if (loopback) {
  2267.                 flags |= Flags.LoopbackHost;
  2268.             }
  2269.             return host;
  2270.         }
  2271.         //
  2272.         // Called under lock()
  2273.         //
  2274.         unsafe private void GetHostViaCustomSyntax()
  2275.         {
  2276.             // A multithreading check
  2277.             if (m_Info.Host != null)
  2278.                 return;
  2279.            
  2280.             string host = m_Syntax.InternalGetComponents(this, UriComponents.Host, UriFormat.UriEscaped);
  2281.            
  2282.             // ATTN: Check on whether recursion has not happened
  2283.             if ((object)m_Info.Host == null) {
  2284.                 if (host.Length >= c_MaxUriBufferSize)
  2285.                     throw GetException(ParsingError.SizeLimit);
  2286.                
  2287.                 ParsingError err = ParsingError.None;
  2288.                 Flags flags = m_Flags & ~Flags.HostTypeMask;
  2289.                
  2290.                 fixed (char* pHost = host) {
  2291.                     if (CheckAuthorityHelper(pHost, 0, (ushort)host.Length, ref err, ref flags, m_Syntax) != (ushort)host.Length) {
  2292.                         // We cannot parse the entire host string
  2293.                         flags &= ~Flags.HostTypeMask;
  2294.                         flags |= Flags.UnknownHostType;
  2295.                     }
  2296.                 }
  2297.                
  2298.                 if (err != ParsingError.None || (flags & Flags.HostTypeMask) == Flags.UnknownHostType) {
  2299.                     // Well, custom parser has returned a not known host type, take it as Basic then.
  2300.                     m_Flags = (m_Flags & ~Flags.HostTypeMask) | Flags.BasicHostType;
  2301.                 }
  2302.                 else {
  2303.                     host = CreateHostStringHelper(host, 0, (ushort)host.Length, ref flags, ref m_Info.ScopeId);
  2304.                     for (ushort i = 0; i < host.Length; ++i) {
  2305.                         if ((m_Info.Offset.Host + i) >= m_Info.Offset.End || host[i] != m_String[m_Info.Offset.Host + i]) {
  2306.                             m_Flags |= (Flags.HostNotCanonical | Flags.E_HostNotCanonical);
  2307.                             break;
  2308.                         }
  2309.                     }
  2310.                     m_Flags = (m_Flags & ~Flags.HostTypeMask) | (flags & Flags.HostTypeMask);
  2311.                 }
  2312.             }
  2313.             //
  2314.             // This is a chance for a custom parser to report a different port value
  2315.             //
  2316.             string portStr = m_Syntax.InternalGetComponents(this, UriComponents.StrongPort, UriFormat.UriEscaped);
  2317.             int port = 0;
  2318.             if ((object)portStr == null || portStr.Length == 0) {
  2319.                 // It's like no port
  2320.                 m_Flags &= ~Flags.NotDefaultPort;
  2321.                 m_Flags |= (Flags.PortNotCanonical | Flags.E_PortNotCanonical);
  2322.                 m_Info.Offset.PortValue = 0;
  2323.             }
  2324.             else {
  2325.                 for (int idx = 0; idx < portStr.Length; ++idx) {
  2326.                     int val = portStr[idx] - '0';
  2327.                     if (val < 0 || val > 9 || (port = (port * 10 + val)) > 65535)
  2328.                         throw new UriFormatException(SR.GetString(SR.net_uri_PortOutOfRange, m_Syntax.GetType().FullName, portStr));
  2329.                 }
  2330.                 if (port != m_Info.Offset.PortValue) {
  2331.                     if (port == m_Syntax.DefaultPort)
  2332.                         m_Flags &= ~Flags.NotDefaultPort;
  2333.                     else
  2334.                         m_Flags |= Flags.NotDefaultPort;
  2335.                    
  2336.                     m_Flags |= (Flags.PortNotCanonical | Flags.E_PortNotCanonical);
  2337.                     m_Info.Offset.PortValue = (ushort)port;
  2338.                 }
  2339.             }
  2340.             // This must be done as the last thing in this method
  2341.             m_Info.Host = host;
  2342.         }
  2343.         //
  2344.         // An internal shortcut into Uri extenisiblity API
  2345.         //
  2346.         internal string GetParts(UriComponents uriParts, UriFormat formatAs)
  2347.         {
  2348.             return GetComponents(uriParts, formatAs);
  2349.         }
  2350.        
  2351.         //
  2352.         //
  2353.         //
  2354.         private string GetEscapedParts(UriComponents uriParts)
  2355.         {
  2356.             // Which Uri parts are not escaped canonically ?
  2357.             // Notice that public UriPart and private Flags must me in Sync so below code can work
  2358.             //
  2359.             ushort nonCanonical = (ushort)(((ushort)m_Flags & ((ushort)Flags.CannotDisplayCanonical << 7)) >> 6);
  2360.             if (InFact(Flags.SchemeNotCanonical)) {
  2361.                 nonCanonical |= (ushort)Flags.SchemeNotCanonical;
  2362.             }
  2363.            
  2364.             // We keep separate flags for some of path canonicalization facts
  2365.             if ((uriParts & UriComponents.Path) != 0) {
  2366.                 if (InFact(Flags.ShouldBeCompressed | Flags.FirstSlashAbsent | Flags.BackslashInPath)) {
  2367.                     nonCanonical |= (ushort)Flags.PathNotCanonical;
  2368.                 }
  2369.                 else if (IsDosPath && m_String[m_Info.Offset.Path + SecuredPathIndex - 1] == '|') {
  2370.                     // A rare case of c|\
  2371.                     nonCanonical |= (ushort)Flags.PathNotCanonical;
  2372.                 }
  2373.             }
  2374.            
  2375.             if (((ushort)uriParts & nonCanonical) == 0) {
  2376.                 string ret = GetUriPartsFromUserString(uriParts);
  2377.                 if ((object)ret != null) {
  2378.                     return ret;
  2379.                 }
  2380.             }
  2381.            
  2382.             return ReCreateParts(uriParts, nonCanonical, UriFormat.UriEscaped);
  2383.         }
  2384.        
  2385.         private string GetUnescapedParts(UriComponents uriParts, UriFormat formatAs)
  2386.         {
  2387.             // Which Uri parts are not escaped canonically ?
  2388.             // Notice that public UriComponents and private Uri.Flags must me in Sync so below code can work
  2389.             //
  2390.             ushort nonCanonical = (ushort)((ushort)m_Flags & (ushort)Flags.CannotDisplayCanonical);
  2391.            
  2392.             // We keep separate flags for some of path canonicalization facts
  2393.             if ((uriParts & UriComponents.Path) != 0) {
  2394.                 if (InFact(Flags.ShouldBeCompressed | Flags.FirstSlashAbsent | Flags.BackslashInPath)) {
  2395.                     nonCanonical |= (ushort)Flags.PathNotCanonical;
  2396.                 }
  2397.                 else if (IsDosPath && m_String[m_Info.Offset.Path + SecuredPathIndex - 1] == '|') {
  2398.                     // A rare case of c|\
  2399.                     nonCanonical |= (ushort)Flags.PathNotCanonical;
  2400.                 }
  2401.                
  2402.             }
  2403.            
  2404.             if (((ushort)uriParts & nonCanonical) == 0) {
  2405.                 string ret = GetUriPartsFromUserString(uriParts);
  2406.                 if ((object)ret != null) {
  2407.                     return ret;
  2408.                 }
  2409.             }
  2410.            
  2411.             return ReCreateParts(uriParts, nonCanonical, formatAs);
  2412.         }
  2413.        
  2414.         //
  2415.         //
  2416.         //
  2417.         private string ReCreateParts(UriComponents parts, ushort nonCanonical, UriFormat formatAs)
  2418.         {
  2419.             // going hard core
  2420.             EnsureHostString(false);
  2421.             string stemp = (parts & UriComponents.Host) == 0 ? string.Empty : m_Info.Host;
  2422.             // we reserve more space than required because a canonical Ipv6 Host
  2423.             // may take more characteres than in original m_String
  2424.             // Also +3 is for :// and +1 is for absent first slash
  2425.             // Also we may escape every character, hence multiplying by 6
  2426.             int count = (m_Info.Offset.End - m_Info.Offset.User) * (formatAs == UriFormat.UriEscaped ? 6 : 1);
  2427.             char[] chars = new char[stemp.Length + count + m_Syntax.SchemeName.Length + 3 + 1];
  2428.             count = 0;
  2429.            
  2430.             //Scheme and slashes
  2431.             if ((parts & UriComponents.Scheme) != 0) {
  2432.                 m_Syntax.SchemeName.CopyTo(0, chars, count, m_Syntax.SchemeName.Length);
  2433.                 count += m_Syntax.SchemeName.Length;
  2434.                 if (parts != UriComponents.Scheme) {
  2435.                     chars[count++] = ':';
  2436.                     if (InFact(Flags.AuthorityFound)) {
  2437.                         chars[count++] = '/';
  2438.                         chars[count++] = '/';
  2439.                     }
  2440.                 }
  2441.             }
  2442.            
  2443.             //UserInfo
  2444.             if ((parts & UriComponents.UserInfo) != 0 && InFact(Flags.HasUserInfo)) {
  2445.                 if ((nonCanonical & (ushort)UriComponents.UserInfo) != 0) {
  2446.                     switch (formatAs) {
  2447.                         case UriFormat.UriEscaped:
  2448.                             if (NotAny(Flags.UserEscaped)) {
  2449.                                 chars = EscapeString(m_String, m_Info.Offset.User, m_Info.Offset.Host, chars, ref count, true, '?', '#', '%');
  2450.                             }
  2451.                             else {
  2452.                                 if (InFact(Flags.E_UserNotCanonical)) {
  2453.                                 }
  2454.                                 m_String.CopyTo(m_Info.Offset.User, chars, count, m_Info.Offset.Host - m_Info.Offset.User);
  2455.                                 count += (m_Info.Offset.Host - m_Info.Offset.User);
  2456.                             }
  2457.                             break;
  2458.                         case UriFormat.SafeUnescaped:
  2459.                            
  2460.                             chars = UnescapeString(m_String, m_Info.Offset.User, m_Info.Offset.Host - 1, chars, ref count, '@', '/', '\\', InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape);
  2461.                             chars[count++] = '@';
  2462.                             break;
  2463.                         case UriFormat.Unescaped:
  2464.                            
  2465.                             chars = UnescapeString(m_String, m_Info.Offset.User, m_Info.Offset.Host, chars, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.Unescape | UnescapeMode.UnescapeAll);
  2466.                             break;
  2467.                         default:
  2468.                            
  2469.                             //V1ToStringUnescape
  2470.                             chars = UnescapeString(m_String, m_Info.Offset.User, m_Info.Offset.Host, chars, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly);
  2471.                             break;
  2472.                     }
  2473.                 }
  2474.                 else {
  2475.                     UnescapeString(m_String, m_Info.Offset.User, m_Info.Offset.Host, chars, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly);
  2476.                 }
  2477.                 if (parts == UriComponents.UserInfo) {
  2478.                     //strip '@' delimiter
  2479.                     --count;
  2480.                 }
  2481.             }
  2482.            
  2483.             // Host
  2484.             if ((parts & UriComponents.Host) != 0 && stemp.Length != 0) {
  2485.                 UnescapeMode mode;
  2486.                 if (formatAs != UriFormat.UriEscaped && HostType == Flags.BasicHostType && (nonCanonical & (ushort)UriComponents.Host) != 0) {
  2487.                     // only Basic host could be in the escaped form
  2488.                     mode = formatAs == UriFormat.Unescaped ? (UnescapeMode.Unescape | UnescapeMode.UnescapeAll) : (InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape);
  2489.                    
  2490.                 }
  2491.                 else {
  2492.                     mode = UnescapeMode.CopyOnly;
  2493.                 }
  2494.                 chars = UnescapeString(stemp, 0, stemp.Length, chars, ref count, '/', '?', '#', mode);
  2495.                
  2496.                
  2497.                 // A fix up only for SerializationInfo and IpV6 host with a scopeID
  2498.                 if ((parts & UriComponents.SerializationInfoString) != 0 && HostType == Flags.IPv6HostType && (object)m_Info.ScopeId != null) {
  2499.                     m_Info.ScopeId.CopyTo(0, chars, count - 1, m_Info.ScopeId.Length);
  2500.                     count += m_Info.ScopeId.Length;
  2501.                     chars[count - 1] = ']';
  2502.                 }
  2503.             }
  2504.            
  2505.             //Port (always wants a ':' delimiter if got to this method)
  2506.             if ((parts & UriComponents.Port) != 0) {
  2507.                 if ((nonCanonical & (ushort)UriComponents.Port) == 0) {
  2508.                     //take it from m_String
  2509.                     if (InFact(Flags.NotDefaultPort)) {
  2510.                         ushort start = m_Info.Offset.Path;
  2511.                         while (m_String[--start] != ':') {
  2512.                             ;
  2513.                         }
  2514.                         m_String.CopyTo(start, chars, count, m_Info.Offset.Path - start);
  2515.                         count += (m_Info.Offset.Path - start);
  2516.                     }
  2517.                     else if ((parts & UriComponents.StrongPort) != 0 && m_Syntax.DefaultPort != UriParser.NoDefaultPort) {
  2518.                         chars[count++] = ':';
  2519.                         stemp = m_Info.Offset.PortValue.ToString(CultureInfo.InvariantCulture);
  2520.                         stemp.CopyTo(0, chars, count, stemp.Length);
  2521.                         count += stemp.Length;
  2522.                     }
  2523.                 }
  2524.                 else if (InFact(Flags.NotDefaultPort) || ((parts & UriComponents.StrongPort) != 0 && m_Syntax.DefaultPort != UriParser.NoDefaultPort)) {
  2525.                     // recreate string from port value
  2526.                     chars[count++] = ':';
  2527.                     stemp = m_Info.Offset.PortValue.ToString(CultureInfo.InvariantCulture);
  2528.                     stemp.CopyTo(0, chars, count, stemp.Length);
  2529.                     count += stemp.Length;
  2530.                 }
  2531.             }
  2532.            
  2533.             ushort delimiterAwareIndex;
  2534.            
  2535.             //Path
  2536.             if ((parts & UriComponents.Path) != 0) {
  2537.                 chars = GetCanonicalPath(chars, ref count, formatAs);
  2538.                
  2539.                 // (possibly strip the leading '/' delimiter)
  2540.                 if (parts == UriComponents.Path) {
  2541.                     if (InFact(Flags.AuthorityFound) && count != 0 && chars[0] == '/') {
  2542.                         delimiterAwareIndex = 1;
  2543.                         --count;
  2544.                     }
  2545.                     else {
  2546.                         delimiterAwareIndex = 0;
  2547.                     }
  2548.                     return count == 0 ? string.Empty : new string(chars, delimiterAwareIndex, count);
  2549.                 }
  2550.             }
  2551.            
  2552.             //Query (possibly strip the '?' delimiter)
  2553.             if ((parts & UriComponents.Query) != 0 && m_Info.Offset.Query < m_Info.Offset.Fragment) {
  2554.                 delimiterAwareIndex = (ushort)(m_Info.Offset.Query + 1);
  2555.                 if (parts != UriComponents.Query)
  2556.                     chars[count++] = '?';
  2557.                 //see Fragment+1 below
  2558.                 if ((nonCanonical & (ushort)UriComponents.Query) != 0) {
  2559.                     switch (formatAs) {
  2560.                         case UriFormat.UriEscaped:
  2561.                             //Can Assert IsImplicitfile == false
  2562.                             if (NotAny(Flags.UserEscaped))
  2563.                                 chars = EscapeString(m_String, delimiterAwareIndex, m_Info.Offset.Fragment, chars, ref count, true, '#', c_DummyChar, '%');
  2564.                             else {
  2565.                                 UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.Fragment, chars, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly);
  2566.                             }
  2567.                             break;
  2568.                         case V1ToStringUnescape:
  2569.                            
  2570.                            
  2571.                             chars = UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.Fragment, chars, ref count, '#', c_DummyChar, c_DummyChar, (InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape) | UnescapeMode.V1ToStringFlag);
  2572.                             break;
  2573.                         case UriFormat.Unescaped:
  2574.                            
  2575.                            
  2576.                             chars = UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.Fragment, chars, ref count, '#', c_DummyChar, c_DummyChar, (UnescapeMode.Unescape | UnescapeMode.UnescapeAll));
  2577.                             break;
  2578.                         default:
  2579.                            
  2580.                             // UriFormat.SafeUnescaped
  2581.                             chars = UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.Fragment, chars, ref count, '#', c_DummyChar, c_DummyChar, (InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape));
  2582.                             break;
  2583.                     }
  2584.                 }
  2585.                 else {
  2586.                     UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.Fragment, chars, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly);
  2587.                 }
  2588.             }
  2589.            
  2590.             //Fragment (possibly strip the '#' delimiter)
  2591.             if ((parts & UriComponents.Fragment) != 0 && m_Info.Offset.Fragment < m_Info.Offset.End) {
  2592.                 delimiterAwareIndex = (ushort)(m_Info.Offset.Fragment + 1);
  2593.                 if (parts != UriComponents.Fragment)
  2594.                     chars[count++] = '#';
  2595.                 //see Fragment+1 below
  2596.                 if ((nonCanonical & (ushort)UriComponents.Fragment) != 0) {
  2597.                     switch (formatAs) {
  2598.                         case UriFormat.UriEscaped:
  2599.                             if (NotAny(Flags.UserEscaped))
  2600.                                 chars = EscapeString(m_String, delimiterAwareIndex, m_Info.Offset.End, chars, ref count, true, '#', c_DummyChar, '%');
  2601.                             else {
  2602.                                 UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.End, chars, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly);
  2603.                             }
  2604.                             break;
  2605.                         case V1ToStringUnescape:
  2606.                            
  2607.                            
  2608.                             chars = UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.End, chars, ref count, '#', c_DummyChar, c_DummyChar, (InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape) | UnescapeMode.V1ToStringFlag);
  2609.                             break;
  2610.                         case UriFormat.Unescaped:
  2611.                            
  2612.                             chars = UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.End, chars, ref count, '#', c_DummyChar, c_DummyChar, UnescapeMode.Unescape | UnescapeMode.UnescapeAll);
  2613.                             break;
  2614.                         default:
  2615.                            
  2616.                             // UriFormat.SafeUnescaped
  2617.                             chars = UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.End, chars, ref count, '#', c_DummyChar, c_DummyChar, (InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape));
  2618.                             break;
  2619.                     }
  2620.                 }
  2621.                 else {
  2622.                     UnescapeString(m_String, delimiterAwareIndex, m_Info.Offset.End, chars, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly);
  2623.                 }
  2624.             }
  2625.            
  2626.             return new string(chars, 0, count);
  2627.         }
  2628.        
  2629.         //
  2630.         // This method is called only if the user string has a canonical representation
  2631.         // of requested parts
  2632.         //
  2633.         private string GetUriPartsFromUserString(UriComponents uriParts)
  2634.         {
  2635.            
  2636.             ushort delimiterAwareIdx;
  2637.            
  2638.             switch (uriParts & ~UriComponents.KeepDelimiter) {
  2639.                 case UriComponents.Scheme | UriComponents.Host | UriComponents.Port:
  2640.                     // For FindServicePoint perf
  2641.                     if (!InFact(Flags.HasUserInfo))
  2642.                         return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.Path - m_Info.Offset.Scheme);
  2643.                    
  2644.                     return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.User - m_Info.Offset.Scheme) + m_String.Substring(m_Info.Offset.Host, m_Info.Offset.Path - m_Info.Offset.Host);
  2645.                 case UriComponents.HostAndPort:
  2646.                    
  2647.                     // For HttpWebRequest.ConnectHostAndPort perf
  2648.                     //Host|StrongPort
  2649.                     if (!InFact(Flags.HasUserInfo))
  2650.                         goto case UriComponents.StrongAuthority;
  2651.                    
  2652.                     if (InFact(Flags.NotDefaultPort) || m_Syntax.DefaultPort == UriParser.NoDefaultPort)
  2653.                         return m_String.Substring(m_Info.Offset.Host, m_Info.Offset.Path - m_Info.Offset.Host);
  2654.                    
  2655.                     return m_String.Substring(m_Info.Offset.Host, m_Info.Offset.Path - m_Info.Offset.Host) + ':' + m_Info.Offset.PortValue.ToString(CultureInfo.InvariantCulture);
  2656.                 case UriComponents.AbsoluteUri:
  2657.                    
  2658.                     // For an obvious common case perf
  2659.                     //Scheme|UserInfo|Host|Port|Path|Query|Fragment,
  2660.                     if (m_Info.Offset.Scheme == 0 && m_Info.Offset.End == m_String.Length)
  2661.                         return m_String;
  2662.                    
  2663.                     return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.End - m_Info.Offset.Scheme);
  2664.                 case UriComponents.HttpRequestUrl:
  2665.                    
  2666.                     // For Uri.Equals() and HttpWebRequest through a proxy perf
  2667.                     //Scheme|Host|Port|Path|Query,
  2668.                     if (InFact(Flags.HasUserInfo)) {
  2669.                         return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.User - m_Info.Offset.Scheme) + m_String.Substring(m_Info.Offset.Host, m_Info.Offset.Fragment - m_Info.Offset.Host);
  2670.                     }
  2671.                     if (m_Info.Offset.Scheme == 0 && m_Info.Offset.Fragment == m_String.Length)
  2672.                         return m_String;
  2673.                    
  2674.                     return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.Fragment - m_Info.Offset.Scheme);
  2675.                 case UriComponents.SchemeAndServer | UriComponents.UserInfo:
  2676.                    
  2677.                     // For CombineUri() perf
  2678.                     return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.Path - m_Info.Offset.Scheme);
  2679.                 case (UriComponents.AbsoluteUri & ~UriComponents.Fragment):
  2680.                    
  2681.                     // For Cache perf
  2682.                     if (m_Info.Offset.Scheme == 0 && m_Info.Offset.Fragment == m_String.Length)
  2683.                         return m_String;
  2684.                    
  2685.                     return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.Fragment - m_Info.Offset.Scheme);
  2686.                 case UriComponents.Scheme:
  2687.                    
  2688.                    
  2689.                     // Strip scheme delimiter if was not requested
  2690.                     if (uriParts != UriComponents.Scheme)
  2691.                         return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.User - m_Info.Offset.Scheme);
  2692.                    
  2693.                     return m_Syntax.SchemeName;
  2694.                 case UriComponents.Host:
  2695.                    
  2696.                     // KeepDelimiter makes no sense for this component
  2697.                     ushort idx = m_Info.Offset.Path;
  2698.                     if (InFact(Flags.NotDefaultPort | Flags.PortNotCanonical)) {
  2699.                         //Means we do have ':' after the host
  2700.                         while (m_String[--idx] != ':')
  2701.                             ;
  2702.                     }
  2703.                     return (idx - m_Info.Offset.Host == 0) ? string.Empty : m_String.Substring(m_Info.Offset.Host, idx - m_Info.Offset.Host);
  2704.                 case UriComponents.Path:
  2705.                    
  2706.                    
  2707.                     // Strip the leading '/' for a hierarchical URI if no delimiter was requested
  2708.                     if (uriParts == UriComponents.Path && InFact(Flags.AuthorityFound) && m_Info.Offset.End > m_Info.Offset.Path && m_String[m_Info.Offset.Path] == '/')
  2709.                         delimiterAwareIdx = (ushort)(m_Info.Offset.Path + 1);
  2710.                     else
  2711.                         delimiterAwareIdx = m_Info.Offset.Path;
  2712.                    
  2713.                     if (delimiterAwareIdx >= m_Info.Offset.Query)
  2714.                         return string.Empty;
  2715.                    
  2716.                    
  2717.                     return m_String.Substring(delimiterAwareIdx, m_Info.Offset.Query - delimiterAwareIdx);
  2718.                 case UriComponents.Query:
  2719.                    
  2720.                     // Strip the '?' if no delimiter was requested
  2721.                     if (uriParts == UriComponents.Query)
  2722.                         delimiterAwareIdx = (ushort)(m_Info.Offset.Query + 1);
  2723.                     else
  2724.                         delimiterAwareIdx = m_Info.Offset.Query;
  2725.                    
  2726.                     if (delimiterAwareIdx >= m_Info.Offset.Fragment)
  2727.                         return string.Empty;
  2728.                    
  2729.                     return m_String.Substring(delimiterAwareIdx, m_Info.Offset.Fragment - delimiterAwareIdx);
  2730.                 case UriComponents.Fragment:
  2731.                    
  2732.                     // Strip the '#' if no delimiter was requested
  2733.                     if (uriParts == UriComponents.Fragment)
  2734.                         delimiterAwareIdx = (ushort)(m_Info.Offset.Fragment + 1);
  2735.                     else
  2736.                         delimiterAwareIdx = m_Info.Offset.Fragment;
  2737.                    
  2738.                     if (delimiterAwareIdx >= m_Info.Offset.End)
  2739.                         return string.Empty;
  2740.                    
  2741.                     return m_String.Substring(delimiterAwareIdx, m_Info.Offset.End - delimiterAwareIdx);
  2742.                 case UriComponents.UserInfo | UriComponents.Host | UriComponents.Port:
  2743.                    
  2744.                     return (m_Info.Offset.Path - m_Info.Offset.User == 0) ? string.Empty : m_String.Substring(m_Info.Offset.User, m_Info.Offset.Path - m_Info.Offset.User);
  2745.                 case UriComponents.StrongAuthority:
  2746.                    
  2747.                     //UserInfo|Host|StrongPort
  2748.                     if (InFact(Flags.NotDefaultPort) || m_Syntax.DefaultPort == UriParser.NoDefaultPort)
  2749.                         goto case UriComponents.UserInfo | UriComponents.Host | UriComponents.Port;
  2750.                    
  2751.                     return m_String.Substring(m_Info.Offset.User, m_Info.Offset.Path - m_Info.Offset.User) + ':' + m_Info.Offset.PortValue.ToString(CultureInfo.InvariantCulture);
  2752.                 case UriComponents.PathAndQuery:
  2753.                    
  2754.                     //Path|Query,
  2755.                     return m_String.Substring(m_Info.Offset.Path, m_Info.Offset.Fragment - m_Info.Offset.Path);
  2756.                 case UriComponents.HttpRequestUrl | UriComponents.Fragment:
  2757.                    
  2758.                     //Scheme|Host|Port|Path|Query|Fragment,
  2759.                     if (InFact(Flags.HasUserInfo)) {
  2760.                         return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.User - m_Info.Offset.Scheme) + m_String.Substring(m_Info.Offset.Host, m_Info.Offset.End - m_Info.Offset.Host);
  2761.                     }
  2762.                     if (m_Info.Offset.Scheme == 0 && m_Info.Offset.End == m_String.Length)
  2763.                         return m_String;
  2764.                    
  2765.                     return m_String.Substring(m_Info.Offset.Scheme, m_Info.Offset.End - m_Info.Offset.Scheme);
  2766.                 case UriComponents.PathAndQuery | UriComponents.Fragment:
  2767.                    
  2768.                     //LocalUrl|Fragment
  2769.                     return m_String.Substring(m_Info.Offset.Path, m_Info.Offset.End - m_Info.Offset.Path);
  2770.                 case UriComponents.UserInfo:
  2771.                    
  2772.                     // Strip the '@' if no delimiter was requested
  2773.                    
  2774.                     if (NotAny(Flags.HasUserInfo))
  2775.                         return string.Empty;
  2776.                    
  2777.                     if (uriParts == UriComponents.UserInfo)
  2778.                         delimiterAwareIdx = (ushort)(m_Info.Offset.Host - 1);
  2779.                     else
  2780.                         delimiterAwareIdx = m_Info.Offset.Host;
  2781.                    
  2782.                     if (m_Info.Offset.User >= delimiterAwareIdx)
  2783.                         return string.Empty;
  2784.                    
  2785.                     return m_String.Substring(m_Info.Offset.User, delimiterAwareIdx - m_Info.Offset.User);
  2786.                 default:
  2787.                    
  2788.                     return null;
  2789.             }
  2790.         }
  2791.        
  2792.        
  2793.         //
  2794.         //This method does:
  2795.         // - Creates m_Info member
  2796.         // - checks all componenets up to path on their canonical representation
  2797.         // - continues parsing starting the path position
  2798.         // - Sets the offsets of remaining components
  2799.         // - Sets the Canonicalization flags if applied
  2800.         // - Will NOT create MoreInfo members
  2801.         //
  2802.         unsafe private void ParseRemaining()
  2803.         {
  2804.            
  2805.             // ensure we parsed up to the path
  2806.             EnsureUriInfo();
  2807.            
  2808.             Flags cF = Flags.Zero;
  2809.            
  2810.             if (UserDrivenParsing)
  2811.                 goto Done;
  2812.            
  2813.             // Multithreading!
  2814.             // m_Info.Offset values may be parsed twice but we lock only on m_Flags update.
  2815.            
  2816.             fixed (char* str = m_String) {
  2817.                 ushort idx = m_Info.Offset.Scheme;
  2818.                 ushort length = (ushort)m_String.Length;
  2819.                
  2820.                 // Cut trailing spaces in m_String
  2821.                 if (length > idx && IsLWS(str[length - 1])) {
  2822.                     --length;
  2823.                     while (length != idx && IsLWS(str[--length]))
  2824.                         ;
  2825.                     ++length;
  2826.                 }
  2827.                
  2828.                 if (IsImplicitFile) {
  2829.                     cF |= Flags.SchemeNotCanonical;
  2830.                 }
  2831.                 else {
  2832.                     ushort i = 0;
  2833.                     for (; i < (ushort)m_Syntax.SchemeName.Length; ++i) {
  2834.                         if (m_Syntax.SchemeName[i] != str[idx + i])
  2835.                             cF |= Flags.SchemeNotCanonical;
  2836.                     }
  2837.                     // For an authority Uri only // after the scheme would be canonical
  2838.                     // (compatibility bug http:\\host)
  2839.                     if (InFact(Flags.AuthorityFound) && (idx + i + 3 >= length || str[idx + i + 1] != '/' || str[idx + i + 2] != '/')) {
  2840.                         cF |= Flags.SchemeNotCanonical;
  2841.                     }
  2842.                 }
  2843.                
  2844.                 Check result = Check.None;
  2845.                
  2846.                 //Check the form of the user info
  2847.                 if (InFact(Flags.HasUserInfo)) {
  2848.                     idx = m_Info.Offset.User;
  2849.                     result = CheckCanonical(str, ref idx, m_Info.Offset.Host, '@');
  2850.                     if ((result & Check.DisplayCanonical) == 0) {
  2851.                         cF |= Flags.UserNotCanonical;
  2852.                     }
  2853.                     if ((result & (Check.EscapedCanonical | Check.BackslashInPath)) != Check.EscapedCanonical) {
  2854.                         cF |= Flags.E_UserNotCanonical;
  2855.                     }
  2856.                 }
  2857.                
  2858.                 //
  2859.                 // Delay canonical Host checking to avoid creation of a host string
  2860.                 // Will do that on demand.
  2861.                 //
  2862.                
  2863.                
  2864.                 //
  2865.                 //We have already checked on the port in EnsureUriInfo() that calls CreateUriInfo
  2866.                 //
  2867.                
  2868.                 //
  2869.                 // Parsing the Path if any
  2870.                 //
  2871.                 idx = m_Info.Offset.Path;
  2872.                 //Some uris do not have a query
  2873.                 // When '?' is passed as delimiter, then it's special case
  2874.                 // so both '?' and '#' will work as delimiters
  2875.                 if (IsImplicitFile || !m_Syntax.InFact(UriSyntaxFlags.MayHaveQuery | UriSyntaxFlags.MayHaveFragment)) {
  2876.                     result = CheckCanonical(str, ref idx, length, c_DummyChar);
  2877.                 }
  2878.                 else {
  2879.                     result = CheckCanonical(str, ref idx, length, (m_Syntax.InFact(UriSyntaxFlags.MayHaveQuery) ? '?' : m_Syntax.InFact(UriSyntaxFlags.MayHaveFragment) ? '#' : c_EOL));
  2880.                 }
  2881.                
  2882.                 // ATTN:
  2883.                 // This may render problems for unknown schemes, but in general for an authority based Uri
  2884.                 // (that has slashes) a path should start with "/"
  2885.                 // This becomes more interesting knowning how a file uri is used in "file://c:/path"
  2886.                 // It will be converted to file:///c:/path
  2887.                 //
  2888.                 // However, even more interesting is that vsmacros://c:\path will not add the third slash in the _canoical_ case
  2889.                 // (vsmacros inventors have violated the RFC)
  2890.                 //
  2891.                 // We use special syntax flag to check if the path is rooted, i.e. has a first slash
  2892.                 //
  2893.                 if (InFact(Flags.AuthorityFound) && m_Syntax.InFact(UriSyntaxFlags.PathIsRooted) && (m_Info.Offset.Path == length || (str[m_Info.Offset.Path] != '/' && str[m_Info.Offset.Path] != '\\'))) {
  2894.                     cF |= Flags.FirstSlashAbsent;
  2895.                 }
  2896.                
  2897.                 // Check the need for compression or backslashes conversion
  2898.                 // we included IsDosPath since it may come with other than FILE uri, for ex. scheme://C:\path
  2899.                 // (This is very unfortunate that the original design has included that feature)
  2900.                 if (IsDosPath || (InFact(Flags.AuthorityFound) && m_Syntax.InFact(UriSyntaxFlags.CompressPath | UriSyntaxFlags.ConvertPathSlashes | UriSyntaxFlags.UnEscapeDotsAndSlashes))) {
  2901.                     if (m_Syntax.InFact(UriSyntaxFlags.UnEscapeDotsAndSlashes) && (result & Check.DotSlashEscaped) != 0)
  2902.                         cF |= (Flags.E_PathNotCanonical | Flags.PathNotCanonical);
  2903.                    
  2904.                     if (m_Syntax.InFact(UriSyntaxFlags.ConvertPathSlashes) && (result & Check.BackslashInPath) != 0)
  2905.                         cF |= (Flags.E_PathNotCanonical | Flags.PathNotCanonical);
  2906.                    
  2907.                     if (m_Syntax.InFact(UriSyntaxFlags.CompressPath) && ((cF & Flags.E_PathNotCanonical) != 0 || (result & Check.DotSlashAttn) != 0))
  2908.                         cF |= Flags.ShouldBeCompressed;
  2909.                    
  2910.                     if ((result & Check.BackslashInPath) != 0)
  2911.                         cF |= Flags.BackslashInPath;
  2912.                 }
  2913.                 else if ((result & Check.BackslashInPath) != 0) {
  2914.                     // for a "generic" path '\' should be escaped
  2915.                     cF |= Flags.E_PathNotCanonical;
  2916.                 }
  2917.                
  2918.                 if ((result & Check.DisplayCanonical) == 0) {
  2919.                     if (NotAny(Flags.ImplicitFile) || InFact(Flags.UserEscaped) || (result & Check.ReservedFound) != 0) {
  2920.                         //means it's found as escaped or has unescaped Reserved Characters
  2921.                         cF |= Flags.PathNotCanonical;
  2922.                     }
  2923.                 }
  2924.                
  2925.                 if (InFact(Flags.ImplicitFile) && (result & (Check.ReservedFound | Check.EscapedCanonical)) != 0) {
  2926.                     // need to escape reserved chars or re-escape '%' if an "escaped sequence" was found
  2927.                     result &= ~Check.EscapedCanonical;
  2928.                 }
  2929.                
  2930.                 if ((result & Check.EscapedCanonical) == 0) {
  2931.                     //means it's found as not completely escaped
  2932.                     cF |= Flags.E_PathNotCanonical;
  2933.                 }
  2934.                
  2935.                 //
  2936.                 //Now we've got to parse the Query if any. Note that Query requires the presense of '?'
  2937.                 //
  2938.                 m_Info.Offset.Query = idx;
  2939.                 if (idx < length && str[idx] == '?') {
  2940.                     ++idx;
  2941.                     // This is to exclude first '?' character from checking
  2942.                     result = CheckCanonical(str, ref idx, length, m_Syntax.InFact(UriSyntaxFlags.MayHaveFragment) ? '#' : c_EOL);
  2943.                     if ((result & Check.DisplayCanonical) == 0) {
  2944.                         cF |= Flags.QueryNotCanonical;
  2945.                     }
  2946.                    
  2947.                     if ((result & (Check.EscapedCanonical | Check.BackslashInPath)) != Check.EscapedCanonical) {
  2948.                         cF |= Flags.E_QueryNotCanonical;
  2949.                     }
  2950.                 }
  2951.                
  2952.                 //
  2953.                 //Now we've got to parse the Fragment if any. Note that Fragment requires the presense of '#'
  2954.                 //
  2955.                
  2956.                 m_Info.Offset.Fragment = idx;
  2957.                 if (idx < length && str[idx] == '#') {
  2958.                     ++idx;
  2959.                     // This is to exclude first '#' character from checking
  2960.                     result = CheckCanonical(str, ref idx, length, c_EOL);
  2961.                     //We don't using c_DummyChar since want to allow '?' and '#' as unescaped
  2962.                     if ((result & Check.DisplayCanonical) == 0) {
  2963.                         cF |= Flags.FragmentNotCanonical;
  2964.                     }
  2965.                    
  2966.                     if ((result & (Check.EscapedCanonical | Check.BackslashInPath)) != Check.EscapedCanonical) {
  2967.                         cF |= Flags.E_FragmentNotCanonical;
  2968.                     }
  2969.                 }
  2970.                
  2971.                 m_Info.Offset.End = idx;
  2972.             }
  2973.             Done:
  2974.             cF |= Flags.AllUriInfoSet;
  2975.             lock (m_Info) {
  2976.                 m_Flags |= cF;
  2977.             }
  2978.         }
  2979.        
  2980.         //
  2981.         //
  2982.         // verifies the syntax of the scheme part
  2983.         // Checks on implicit File: scheme due to simple Dos/Unc path passed
  2984.         // returns the start of the next component position
  2985.         // throws UriFormatException if invalid scheme
  2986.         //
  2987.         unsafe private static ushort ParseSchemeCheckImplicitFile(char* uriString, ushort length, ref ParsingError err, ref Flags flags, ref UriParser syntax)
  2988.         {
  2989.            
  2990.             ushort idx = 0;
  2991.            
  2992.             //skip whitespaces
  2993.             while (idx < length && IsLWS(uriString[idx])) {
  2994.                 ++idx;
  2995.             }
  2996.            
  2997.            
  2998.             ushort end = idx;
  2999.             while (end < length && uriString[end] != ':') {
  3000.                 ++end;
  3001.             }
  3002.            
  3003.             // NB: On 64-bits we will use less optimized code from CheckSchemeSyntax()
  3004.             //
  3005.             if (IntPtr.Size == 4) {
  3006.                 // long = 4chars: The minimal size of a known scheme is 3 + ':'
  3007.                 if (end != length && end >= idx + 3 && CheckKnownSchemes((long*)(uriString + idx), (ushort)(end - idx), ref syntax)) {
  3008.                     return (ushort)(end + 1);
  3009.                 }
  3010.             }
  3011.            
  3012.             //NB: A string must have at least 3 characters and at least 1 before ':'
  3013.             if (idx + 2 >= length || end == idx) {
  3014.                 err = ParsingError.BadFormat;
  3015.                 return 0;
  3016.             }
  3017.            
  3018.             //Check for supported special cases like a DOS file path OR a UNC share path
  3019.             //NB: A string may not have ':' if this is a UNC path
  3020.             {
  3021.                 char c;
  3022.                 if ((c = uriString[idx + 1]) == ':' || c == '|') {
  3023.                     #if !PLATFORM_UNIX
  3024.                     //DOS-like path?
  3025.                     if (IsAsciiLetter(uriString[idx])) {
  3026.                         if ((c = uriString[idx + 2]) == '\\' || c == '/') {
  3027.                             flags |= (Flags.DosPath | Flags.ImplicitFile | Flags.AuthorityFound);
  3028.                             syntax = UriParser.FileUri;
  3029.                             return idx;
  3030.                         }
  3031.                         err = ParsingError.MustRootedPath;
  3032.                         return 0;
  3033.                     }
  3034.                     #endif // !PLATFORM_UNIX
  3035.                     err = ParsingError.BadFormat;
  3036.                     return 0;
  3037.                 }
  3038.                 #if !PLATFORM_UNIX
  3039.                 else if ((c = uriString[idx]) == '/' || c == '\\') {
  3040.                     //UNC share ?
  3041.                     if ((c = uriString[idx + 1]) == '\\' || c == '/') {
  3042.                         flags |= (Flags.UncPath | Flags.ImplicitFile | Flags.AuthorityFound);
  3043.                         syntax = UriParser.FileUri;
  3044.                         idx += 2;
  3045.                         // V1.1 compat this will simply eat any slashes prepended to a UNC path
  3046.                         while (idx < length && ((c = uriString[idx]) == '/' || c == '\\'))
  3047.                             ++idx;
  3048.                        
  3049.                         return idx;
  3050.                     }
  3051.                     err = ParsingError.BadFormat;
  3052.                     return 0;
  3053.                 }
  3054.                 #else
  3055.                 else if (uriString[idx] == '/') {
  3056.                     // On UNIX an implicit file has the form /<path> or scheme:///<path>
  3057.                     if (idx == 0 || uriString[idx - 1] != ':') {
  3058.                         // No scheme present; implicit /<path> starting at idx
  3059.                         flags |= (Flags.ImplicitFile | Flags.AuthorityFound);
  3060.                         syntax = UriParser.FileUri;
  3061.                         return idx;
  3062.                     }
  3063.                     else if (uriString[idx + 1] == '/' && uriString[idx + 2] == '/') {
  3064.                         // scheme present; rooted path starts at idx + 2
  3065.                         flags |= (Flags.ImplicitFile | Flags.AuthorityFound);
  3066.                         syntax = UriParser.FileUri;
  3067.                         idx += 2;
  3068.                         return idx;
  3069.                     }
  3070.                 }
  3071.                 else if (uriString[idx] == '\\') {
  3072.                     err = ParsingError.BadFormat;
  3073.                     return 0;
  3074.                 }
  3075.                 #endif // !PLATFORM_UNIX
  3076.             }
  3077.            
  3078.             if (end == length) {
  3079.                 err = ParsingError.BadFormat;
  3080.                 return 0;
  3081.             }
  3082.            
  3083.             // Here could be a possibly valid, and not well-known scheme
  3084.             // Finds the scheme delimiter
  3085.             // we don;t work with the schemes names > c_MaxUriSchemeName (should be ~1k)
  3086.             if ((end - idx) > c_MaxUriSchemeName) {
  3087.                 err = ParsingError.SchemeLimit;
  3088.                 return 0;
  3089.             }
  3090.            
  3091.             //Check the syntax, canonicalize and avoid a GC call
  3092.             char* schemePtr = stackalloc char[end - idx];
  3093.             for (length = 0; idx < end; ++idx) {
  3094.                 schemePtr[length++] = uriString[idx];
  3095.             }
  3096.             err = CheckSchemeSyntax(schemePtr, length, ref syntax);
  3097.             if (err != ParsingError.None) {
  3098.                 return 0;
  3099.             }
  3100.             return (ushort)(end + 1);
  3101.         }
  3102.         //
  3103.         // Quickly parses well known schemes.
  3104.         // nChars does not include the last ':'. Assuming there is one at the end of passed buffer
  3105.         unsafe private static bool CheckKnownSchemes(long* lptr, ushort nChars, ref UriParser syntax)
  3106.         {
  3107.             //NOTE beware of too short input buffers!
  3108.            
  3109.             const long _HTTP_Mask0 = 'h' | ('t' << 16) | ((long)'t' << 32) | ((long)'p' << 48);
  3110.             const char _HTTPS_Mask1 = 's';
  3111.             const long _FTP_Mask = 'f' | ('t' << 16) | ((long)'p' << 32) | ((long)':' << 48);
  3112.             const long _FILE_Mask0 = 'f' | ('i' << 16) | ((long)'l' << 32) | ((long)'e' << 48);
  3113.             const long _GOPHER_Mask0 = 'g' | ('o' << 16) | ((long)'p' << 32) | ((long)'h' << 48);
  3114.             const int _GOPHER_Mask1 = 'e' | ('r' << 16);
  3115.             const long _MAILTO_Mask0 = 'm' | ('a' << 16) | ((long)'i' << 32) | ((long)'l' << 48);
  3116.             const int _MAILTO_Mask1 = 't' | ('o' << 16);
  3117.             const long _NEWS_Mask0 = 'n' | ('e' << 16) | ((long)'w' << 32) | ((long)'s' << 48);
  3118.             const long _NNTP_Mask0 = 'n' | ('n' << 16) | ((long)'t' << 32) | ((long)'p' << 48);
  3119.             const long _UUID_Mask0 = 'u' | ('u' << 16) | ((long)'i' << 32) | ((long)'d' << 48);
  3120.            
  3121.             const long _TELNET_Mask0 = 't' | ('e' << 16) | ((long)'l' << 32) | ((long)'n' << 48);
  3122.             const int _TELNET_Mask1 = 'e' | ('t' << 16);
  3123.            
  3124.             const long _NETXXX_Mask0 = 'n' | ('e' << 16) | ((long)'t' << 32) | ((long)'.' << 48);
  3125.             const long _NETTCP_Mask1 = 't' | ('c' << 16) | ((long)'p' << 32) | ((long)':' << 48);
  3126.             const long _NETPIPE_Mask1 = 'p' | ('i' << 16) | ((long)'p' << 32) | ((long)'e' << 48);
  3127.            
  3128.             const long _LDAP_Mask0 = 'l' | ('d' << 16) | ((long)'a' << 32) | ((long)'p' << 48);
  3129.            
  3130.            
  3131.             const long _LOWERCASE_Mask = 9007336695791648l;
  3132.             const int _INT_LOWERCASE_Mask = 2097184;
  3133.            
  3134.            
  3135.             //Map to a known scheme if possible
  3136.             //upgrade 4 letters to ASCII lower case, keep a false case to stay false
  3137.             switch (*lptr | _LOWERCASE_Mask) {
  3138.                 case _HTTP_Mask0:
  3139.                     if (nChars == 4) {
  3140.                         syntax = UriParser.HttpUri;
  3141.                         return true;
  3142.                     }
  3143.                     if (nChars == 5 && ((*(char*)(lptr + 1)) | 32) == _HTTPS_Mask1) {
  3144.                         syntax = UriParser.HttpsUri;
  3145.                         return true;
  3146.                     }
  3147.                     break;
  3148.                 case _FILE_Mask0:
  3149.                    
  3150.                     if (nChars == 4) {
  3151.                         syntax = UriParser.FileUri;
  3152.                         return true;
  3153.                     }
  3154.                     break;
  3155.                 case _FTP_Mask:
  3156.                     if (nChars == 3) {
  3157.                         syntax = UriParser.FtpUri;
  3158.                         return true;
  3159.                     }
  3160.                     break;
  3161.                 case _NEWS_Mask0:
  3162.                    
  3163.                     if (nChars == 4) {
  3164.                         syntax = UriParser.NewsUri;
  3165.                         return true;
  3166.                     }
  3167.                     break;
  3168.                 case _NNTP_Mask0:
  3169.                    
  3170.                     if (nChars == 4) {
  3171.                         syntax = UriParser.NntpUri;
  3172.                         return true;
  3173.                     }
  3174.                     break;
  3175.                 case _UUID_Mask0:
  3176.                    
  3177.                     if (nChars == 4) {
  3178.                         syntax = UriParser.UuidUri;
  3179.                         return true;
  3180.                     }
  3181.                     break;
  3182.                 case _GOPHER_Mask0:
  3183.                    
  3184.                     if (nChars == 6 && (*(int*)(lptr + 1) | _INT_LOWERCASE_Mask) == _GOPHER_Mask1) {
  3185.                         syntax = UriParser.GopherUri;
  3186.                         return true;
  3187.                     }
  3188.                     break;
  3189.                 case _MAILTO_Mask0:
  3190.                     if (nChars == 6 && (*(int*)(lptr + 1) | _INT_LOWERCASE_Mask) == _MAILTO_Mask1) {
  3191.                         syntax = UriParser.MailToUri;
  3192.                         return true;
  3193.                     }
  3194.                     break;
  3195.                 case _TELNET_Mask0:
  3196.                    
  3197.                     if (nChars == 6 && (*(int*)(lptr + 1) | _INT_LOWERCASE_Mask) == _TELNET_Mask1) {
  3198.                         syntax = UriParser.TelnetUri;
  3199.                         return true;
  3200.                     }
  3201.                     break;
  3202.                 case _NETXXX_Mask0:
  3203.                    
  3204.                     if (nChars == 8 && (*(lptr + 1) | _LOWERCASE_Mask) == _NETPIPE_Mask1) {
  3205.                         syntax = UriParser.NetPipeUri;
  3206.                         return true;
  3207.                     }
  3208.                     else if (nChars == 7 && (*(lptr + 1) | _LOWERCASE_Mask) == _NETTCP_Mask1) {
  3209.                         syntax = UriParser.NetTcpUri;
  3210.                         return true;
  3211.                     }
  3212.                     break;
  3213.                 case _LDAP_Mask0:
  3214.                    
  3215.                     if (nChars == 4) {
  3216.                         syntax = UriParser.LdapUri;
  3217.                         return true;
  3218.                     }
  3219.                     break;
  3220.                 default:
  3221.                     break;
  3222.             }
  3223.             return false;
  3224.         }
  3225.        
  3226.         //
  3227.         //
  3228.         // This will check whether a scheme string follows the rules
  3229.         //
  3230.         unsafe private static ParsingError CheckSchemeSyntax(char* ptr, ushort length, ref UriParser syntax)
  3231.         {
  3232.             //First character must be an alpha
  3233.             {
  3234.                 char c = *ptr;
  3235.                 if (c >= 'a' && c <= 'z') {
  3236.                     ;
  3237.                 }
  3238.                 else if (c >= 'A' && c <= 'Z') {
  3239.                     *ptr = (char)(c | 32);
  3240.                     //make it lowercase
  3241.                 }
  3242.                 else {
  3243.                     return ParsingError.BadScheme;
  3244.                 }
  3245.             }
  3246.            
  3247.             for (ushort i = 1; i < length; ++i) {
  3248.                 char c = ptr[i];
  3249.                 if (c >= 'a' && c <= 'z') {
  3250.                     ;
  3251.                 }
  3252.                 else if (c >= 'A' && c <= 'Z') {
  3253.                     ptr[i] = (char)(c | 32);
  3254.                     //make it lowercase
  3255.                 }
  3256.                 else if (c >= '0' && c <= '9') {
  3257.                     ;
  3258.                 }
  3259.                 else if (c == '+' || c == '-' || c == '.') {
  3260.                     ;
  3261.                 }
  3262.                 else {
  3263.                     return ParsingError.BadScheme;
  3264.                 }
  3265.             }
  3266.             // A not well-known scheme, needs string creation
  3267.             // Note it is already in the lower case as required.
  3268.             string str = new string(ptr, 0, length);
  3269.             syntax = UriParser.FindOrFetchAsUnknownV1Syntax(str);
  3270.             return ParsingError.None;
  3271.         }
  3272.         //
  3273.         //
  3274.         // Checks the syntax of an authority component. It may also get a userInfo if present
  3275.         // Returns an error if no/mailformed authority found
  3276.         // Does not NOT touch m_Info
  3277.         // Returns position of the Path component
  3278.         //
  3279.         // Must be called in the ctor only
  3280.         unsafe private static ushort CheckAuthorityHelper(char* pString, ushort idx, ushort length, ref ParsingError err, ref Flags flags, UriParser syntax)
  3281.         {
  3282.             int end = length;
  3283.             char ch;
  3284.            
  3285.             //Special case is an empty authority
  3286.             if (idx == length || ((ch = pString[idx]) == '/' || (ch == '\\' && StaticIsFile(syntax)) || ch == '#' || ch == '?')) {
  3287.                 if (syntax.InFact(UriSyntaxFlags.AllowEmptyHost)) {
  3288.                     flags &= ~Flags.UncPath;
  3289.                     //UNC cannot have an empty hostname
  3290.                     if (StaticInFact(flags, Flags.ImplicitFile))
  3291.                         err = ParsingError.BadHostName;
  3292.                     else
  3293.                         flags |= Flags.BasicHostType;
  3294.                 }
  3295.                 else
  3296.                     err = ParsingError.BadHostName;
  3297.                
  3298.                 return idx;
  3299.             }
  3300.            
  3301.             #if PLATFORM_UNIX
  3302.             if (StaticIsFile(syntax) && ch != '/') {
  3303.                 // On UNIX a file URL may only have an empty authority
  3304.                 err = ParsingError.NonEmptyHost;
  3305.                 return idx;
  3306.             }
  3307.             #endif // PLATFORM_UNIX
  3308.            
  3309.             ushort start = idx;
  3310.            
  3311.             // Attempt to parse user info first
  3312.             if (syntax.InFact(UriSyntaxFlags.MayHaveUserInfo)) {
  3313.                 for (; start < end; ++start) {
  3314.                     if (start == end - 1 || pString[start] == '?' || pString[start] == '#' || pString[start] == '\\' || pString[start] == '/') {
  3315.                         start = idx;
  3316.                         break;
  3317.                     }
  3318.                     else if (pString[start] == '@') {
  3319.                         flags |= Flags.HasUserInfo;
  3320.                         ++start;
  3321.                         ch = pString[start];
  3322.                         break;
  3323.                     }
  3324.                 }
  3325.             }
  3326.            
  3327.             // DNS name only optimization
  3328.             // Fo an overriden parsing the optimization is suppressed since hostname can be changed to anything
  3329.             bool dnsNotCanonical = !syntax.IsSimple;
  3330.            
  3331.             if (ch == '[' && syntax.InFact(UriSyntaxFlags.AllowIPv6Host) && IPv6AddressHelper.IsValid(pString, (int)start + 1, ref end)) {
  3332.                 flags |= Flags.IPv6HostType;
  3333.             }
  3334.             else if (ch <= '9' && ch >= '0' && syntax.InFact(UriSyntaxFlags.AllowIPv4Host) && IPv4AddressHelper.IsValid(pString, (int)start, ref end, false, StaticNotAny(flags, Flags.ImplicitFile))) {
  3335.                 flags |= Flags.IPv4HostType;
  3336.             }
  3337.             else if (syntax.InFact(UriSyntaxFlags.AllowDnsHost) && DomainNameHelper.IsValid(pString, start, ref end, ref dnsNotCanonical, StaticNotAny(flags, Flags.ImplicitFile))) {
  3338.                 flags |= Flags.DnsHostType;
  3339.                 if (!dnsNotCanonical) {
  3340.                     flags |= Flags.CanonicalDnsHost;
  3341.                 }
  3342.             }
  3343.             #if !PLATFORM_UNIX
  3344.             else if (syntax.InFact(UriSyntaxFlags.AllowUncHost)) {
  3345.                 //
  3346.                 // This must remain as the last check befor BasicHost type
  3347.                 //
  3348.                 if (UncNameHelper.IsValid(pString, start, ref end, StaticNotAny(flags, Flags.ImplicitFile))) {
  3349.                     if (end - start <= UncNameHelper.MaximumInternetNameLength)
  3350.                         flags |= Flags.UncHostType;
  3351.                 }
  3352.             }
  3353.             #endif // !PLATFORM_UNIX
  3354.            
  3355.             // The deal here is that we won't allow '\' host terminator except for the File scheme
  3356.             // If we see '\' we try to make it a part of of a Basic host
  3357.             if (end < length && pString[end] == '\\' && (flags & Flags.HostTypeMask) != Flags.HostNotParsed && !StaticIsFile(syntax)) {
  3358.                 if (syntax.InFact(UriSyntaxFlags.V1_UnknownUri)) {
  3359.                     err = ParsingError.BadHostName;
  3360.                     flags |= Flags.UnknownHostType;
  3361.                     return (ushort)end;
  3362.                 }
  3363.                 flags &= ~Flags.HostTypeMask;
  3364.             }
  3365.             // Here we have checked the syntax up to the end of host
  3366.             // The only thing that can cause an exception is the port value
  3367.             // Spend some (duplicated) cycles on that.
  3368.             else if (end < length && pString[end] == ':') {
  3369.                 if (syntax.InFact(UriSyntaxFlags.MayHavePort)) {
  3370.                     int port = 0;
  3371.                     for (idx = (ushort)(end + 1); idx < length; ++idx) {
  3372.                         ushort val = (ushort)((ushort)pString[idx] - (ushort)'0');
  3373.                         if ((val >= 0) && (val <= 9)) {
  3374.                             if ((port = (port * 10 + val)) > 65535)
  3375.                                 break;
  3376.                         }
  3377.                         else if (val == unchecked((ushort)('/' - '0')) || val == (ushort)('?' - '0') || val == unchecked((ushort)('#' - '0'))) {
  3378.                             break;
  3379.                         }
  3380.                         else {
  3381.                             // The second check is to keep compatibility with V1 until the UriParser is registered
  3382.                             if (syntax.InFact(UriSyntaxFlags.AllowAnyOtherHost) && syntax.NotAny(UriSyntaxFlags.V1_UnknownUri)) {
  3383.                                 flags &= ~Flags.HostTypeMask;
  3384.                                 break;
  3385.                             }
  3386.                             else {
  3387.                                 err = ParsingError.BadPort;
  3388.                                 return idx;
  3389.                             }
  3390.                         }
  3391.                     }
  3392.                     // check on 0-ffff range
  3393.                     if (port > 65535) {
  3394.                         if (syntax.InFact(UriSyntaxFlags.AllowAnyOtherHost)) {
  3395.                             flags &= ~Flags.HostTypeMask;
  3396.                         }
  3397.                         else {
  3398.                             err = ParsingError.BadPort;
  3399.                             return idx;
  3400.                         }
  3401.                     }
  3402.                 }
  3403.                 else {
  3404.                     flags &= ~Flags.HostTypeMask;
  3405.                 }
  3406.             }
  3407.            
  3408.             // check on whether nothing has worked out
  3409.             if ((flags & Flags.HostTypeMask) == Flags.HostNotParsed) {
  3410.                 //No user info for a Basic hostname
  3411.                 flags &= ~Flags.HasUserInfo;
  3412.                 // Some schemes do not allow HostType = Basic (plus V1 almost never understands this cause of a bug)
  3413.                 //
  3414.                 if (syntax.InFact(UriSyntaxFlags.AllowAnyOtherHost)) {
  3415.                     flags |= Flags.BasicHostType;
  3416.                     for (end = idx; end < length; ++end) {
  3417.                         if (pString[end] == '/' || (pString[end] == '?' || pString[end] == '#')) {
  3418.                             break;
  3419.                         }
  3420.                     }
  3421.                 }
  3422.                 else {
  3423.                     if (syntax.InFact(UriSyntaxFlags.V1_UnknownUri)) {
  3424.                         // Can assert here that the host is not empty so we will set dotFound
  3425.                         // at least once or fail before exiting the loop
  3426.                         bool dotFound = false;
  3427.                         for (end = idx; end < length; ++end) {
  3428.                             if (dotFound && (pString[end] == '/' || pString[end] == '?' || pString[end] == '#'))
  3429.                                 break;
  3430.                             else if (end < (idx + 2) && pString[end] == '.') {
  3431.                                 // allow one or two dots
  3432.                                 dotFound = true;
  3433.                             }
  3434.                             else {
  3435.                                 //failure
  3436.                                 err = ParsingError.BadHostName;
  3437.                                 flags |= Flags.UnknownHostType;
  3438.                                 return idx;
  3439.                             }
  3440.                         }
  3441.                         //success
  3442.                         flags |= Flags.BasicHostType;
  3443.                     }
  3444.                     else if (syntax.InFact(UriSyntaxFlags.MustHaveAuthority)) {
  3445.                         err = ParsingError.BadHostName;
  3446.                         flags |= Flags.UnknownHostType;
  3447.                         return idx;
  3448.                     }
  3449.                 }
  3450.             }
  3451.             return (ushort)end;
  3452.         }
  3453.        
  3454.        
  3455.         //
  3456.         //
  3457.         // The method checks whether a string needs transformation before going to display or wire
  3458.         //
  3459.         // Parameters:
  3460.         // - escaped true = treat all valid escape sequences as escaped sequences, false = escape all %
  3461.         // - delim a character signalling the termination of the component being checked
  3462.         //
  3463.         // When delim=='?', then '#' character is also considered as delimiter additionally to passed '?'.
  3464.         //
  3465.         // The method pays attention to the dots and slashes so to signal potential Path compression action needed.
  3466.         // Even that is not required for other components, the cycles are still spent (little inefficiency)
  3467.         //
  3468.        
  3469.         const char c_DummyChar = (char)65535;
  3470.         //An Invalid Unicode character used as a dummy char passed into the parameter
  3471.         const char c_EOL = (char)65534;
  3472.         //An Invalid Unicode character used by CheckCanonical as "no delimiter condition"
  3473.         [Flags()]
  3474.         private enum Check
  3475.         {
  3476.             None = 0,
  3477.             EscapedCanonical = 1,
  3478.             DisplayCanonical = 2,
  3479.             DotSlashAttn = 4,
  3480.             DotSlashEscaped = 128,
  3481.             BackslashInPath = 16,
  3482.             ReservedFound = 32
  3483.         }
  3484.        
  3485.         //
  3486.         // Used by ParseRemaining as well by InternalIsWellFormedOriginalString
  3487.         //
  3488.         unsafe private Check CheckCanonical(char* str, ref ushort idx, ushort end, char delim)
  3489.         {
  3490.             Check res = Check.None;
  3491.             bool needsEscaping = false;
  3492.             bool foundEscaping = false;
  3493.            
  3494.             char c = c_DummyChar;
  3495.             ushort i = idx;
  3496.             for (; i < end; ++i) {
  3497.                 c = str[i];
  3498.                 // Control chars usually should be escaped in any case
  3499.                 if (c <= '\u31' || (c >= '\u127' && c <= '\u159')) {
  3500.                     needsEscaping = true;
  3501.                     foundEscaping = true;
  3502.                     res |= Check.ReservedFound;
  3503.                 }
  3504.                 else if (c > 'z' && c != '~') {
  3505.                     if (!needsEscaping)
  3506.                         needsEscaping = true;
  3507.                 }
  3508.                 else if (c == delim) {
  3509.                     break;
  3510.                 }
  3511.                 else if (delim == '?' && c == '#' && (m_Syntax != null && m_Syntax.InFact(UriSyntaxFlags.MayHaveFragment))) {
  3512.                     // this is a special case when deciding on Query/Fragment
  3513.                     break;
  3514.                 }
  3515.                 else if (c == '?') {
  3516.                     if (IsImplicitFile || (m_Syntax != null && !m_Syntax.InFact(UriSyntaxFlags.MayHaveQuery) && delim != c_EOL)) {
  3517.                         res |= Check.ReservedFound;
  3518.                         foundEscaping = true;
  3519.                         needsEscaping = true;
  3520.                     }
  3521.                 }
  3522.                 else if (c == '#') {
  3523.                     needsEscaping = true;
  3524.                     if (IsImplicitFile || (m_Syntax != null && !m_Syntax.InFact(UriSyntaxFlags.MayHaveFragment))) {
  3525.                         res |= Check.ReservedFound;
  3526.                         foundEscaping = true;
  3527.                     }
  3528.                 }
  3529.                 else if (c == '/' || c == '\\') {
  3530.                     if ((res & Check.BackslashInPath) == 0 && c == '\\') {
  3531.                         res |= Check.BackslashInPath;
  3532.                     }
  3533.                     if ((res & Check.DotSlashAttn) == 0 && i + 1 != end && (str[i + 1] == '/' || str[i + 1] == '\\')) {
  3534.                         res |= Check.DotSlashAttn;
  3535.                     }
  3536.                 }
  3537.                 else if (c == '.') {
  3538.                     if ((res & Check.DotSlashAttn) == 0 && i + 1 == end || str[i + 1] == '.' || str[i + 1] == '/' || str[i + 1] == '\\' || str[i + 1] == '?' || str[i + 1] == '#') {
  3539.                         res |= Check.DotSlashAttn;
  3540.                     }
  3541.                 }
  3542.                 else if (!needsEscaping && ((c <= '"' && c != '!') || (c >= '[' && c <= '^') || c == '>' || c == '<' || c == '`')) {
  3543.                     needsEscaping = true;
  3544.                 }
  3545.                 else if (c == '%') {
  3546.                     if (!foundEscaping)
  3547.                         foundEscaping = true;
  3548.                     //try unescape a byte hex escaping
  3549.                     if (i + 2 < end && (c = EscapedAscii(str[i + 1], str[i + 2])) != c_DummyChar) {
  3550.                         if (c == '.' || c == '/' || c == '\\') {
  3551.                             res |= Check.DotSlashEscaped;
  3552.                         }
  3553.                         i += 2;
  3554.                         continue;
  3555.                     }
  3556.                     // otherwise we follow to non escaped case
  3557.                     if (!needsEscaping) {
  3558.                         needsEscaping = true;
  3559.                     }
  3560.                 }
  3561.             }
  3562.            
  3563.             if (foundEscaping) {
  3564.                 if (!needsEscaping) {
  3565.                     res |= Check.EscapedCanonical;
  3566.                 }
  3567.             }
  3568.             else {
  3569.                 res |= Check.DisplayCanonical;
  3570.                 if (!needsEscaping) {
  3571.                     res |= Check.EscapedCanonical;
  3572.                 }
  3573.             }
  3574.             idx = i;
  3575.             return res;
  3576.         }
  3577.        
  3578.         //
  3579.         // Returns the escaped and canonicalized path string
  3580.         // the passed array must be long enough to hold at least
  3581.         // canonical unescaped path representation (allocated by the caller)
  3582.         //
  3583.         unsafe private char[] GetCanonicalPath(char[] dest, ref int pos, UriFormat formatAs)
  3584.         {
  3585.            
  3586.             if (InFact(Flags.FirstSlashAbsent))
  3587.                 dest[pos++] = '/';
  3588.            
  3589.             if (m_Info.Offset.Path == m_Info.Offset.Query)
  3590.                 return dest;
  3591.            
  3592.             int end = pos;
  3593.            
  3594.             int dosPathIdx = SecuredPathIndex;
  3595.            
  3596.             if (formatAs == UriFormat.UriEscaped) {
  3597.                 if (InFact(Flags.ShouldBeCompressed)) {
  3598.                     m_String.CopyTo(m_Info.Offset.Path, dest, end, m_Info.Offset.Query - m_Info.Offset.Path);
  3599.                     end += (m_Info.Offset.Query - m_Info.Offset.Path);
  3600.                    
  3601.                     // If the path was found as needed compression and contains escaped characters, unescape only interesting characters (safe)
  3602.                    
  3603.                     if (m_Syntax.InFact(UriSyntaxFlags.UnEscapeDotsAndSlashes) && InFact(Flags.PathNotCanonical) && !IsImplicitFile) {
  3604.                         fixed (char* pdest = dest)
  3605.                             UnescapeOnly(pdest, pos, ref end, '.', '/', m_Syntax.InFact(UriSyntaxFlags.ConvertPathSlashes) ? '\\' : c_DummyChar);
  3606.                     }
  3607.                 }
  3608.                 else {
  3609.                     if (InFact(Flags.E_PathNotCanonical) && NotAny(Flags.UserEscaped)) {
  3610.                         string str = m_String;
  3611.                        
  3612.                         // Check on not canonical disk designation like C|\, should be rare, rare case
  3613.                         if (dosPathIdx != 0 && str[dosPathIdx + m_Info.Offset.Path - 1] == '|') {
  3614.                             str = str.Remove(dosPathIdx + m_Info.Offset.Path - 1, 1);
  3615.                             str = str.Insert(dosPathIdx + m_Info.Offset.Path - 1, ":");
  3616.                         }
  3617.                         dest = EscapeString(str, m_Info.Offset.Path, m_Info.Offset.Query, dest, ref end, true, '?', '#', IsImplicitFile ? c_DummyChar : '%');
  3618.                     }
  3619.                     else {
  3620.                         m_String.CopyTo(m_Info.Offset.Path, dest, end, m_Info.Offset.Query - m_Info.Offset.Path);
  3621.                         end += (m_Info.Offset.Query - m_Info.Offset.Path);
  3622.                     }
  3623.                 }
  3624.             }
  3625.             else {
  3626.                 m_String.CopyTo(m_Info.Offset.Path, dest, end, m_Info.Offset.Query - m_Info.Offset.Path);
  3627.                 end += (m_Info.Offset.Query - m_Info.Offset.Path);
  3628.                
  3629.                 if (InFact(Flags.ShouldBeCompressed)) {
  3630.                     // If the path was found as needed compression and contains escaped characters, unescape only interesting characters (safe)
  3631.                    
  3632.                     if (m_Syntax.InFact(UriSyntaxFlags.UnEscapeDotsAndSlashes) && InFact(Flags.PathNotCanonical) && !IsImplicitFile) {
  3633.                         fixed (char* pdest = dest)
  3634.                             UnescapeOnly(pdest, pos, ref end, '.', '/', m_Syntax.InFact(UriSyntaxFlags.ConvertPathSlashes) ? '\\' : c_DummyChar);
  3635.                     }
  3636.                 }
  3637.             }
  3638.            
  3639.             // Here we already got output data as copied into dest array
  3640.             // We just may need more processing of that data
  3641.            
  3642.             //
  3643.             // if this URI is using 'non-proprietary' disk drive designation, convert to MS-style
  3644.             //
  3645.             // (path is already >= 3 chars if recognized as a DOS-like)
  3646.             //
  3647.             if (dosPathIdx != 0 && dest[dosPathIdx + pos - 1] == '|')
  3648.                 dest[dosPathIdx + pos - 1] = ':';
  3649.            
  3650.             if (InFact(Flags.ShouldBeCompressed)) {
  3651.                 // It will also convert back slashes if needed
  3652.                 dest = Compress(dest, (ushort)(pos + dosPathIdx), ref end, m_Syntax);
  3653.                 if (dest[pos] == '\\')
  3654.                     dest[pos] = '/';
  3655.                
  3656.                 // Escape path if requested and found as not fully escaped
  3657.                 if (formatAs == UriFormat.UriEscaped && NotAny(Flags.UserEscaped) && InFact(Flags.E_PathNotCanonical)) {
  3658.                     string srcString = new string(dest, pos, end - pos);
  3659.                     dest = EscapeString(srcString, 0, end - pos, dest, ref pos, true, '?', '#', IsImplicitFile ? c_DummyChar : '%');
  3660.                     end = pos;
  3661.                 }
  3662.             }
  3663.             else if (m_Syntax.InFact(UriSyntaxFlags.ConvertPathSlashes) && InFact(Flags.BackslashInPath)) {
  3664.                 for (int i = pos; i < end; ++i)
  3665.                     if (dest[i] == '\\')
  3666.                         dest[i] = '/';
  3667.             }
  3668.            
  3669.             if (formatAs != UriFormat.UriEscaped && InFact(Flags.PathNotCanonical)) {
  3670.                 UnescapeMode mode;
  3671.                 if (InFact(Flags.PathNotCanonical)) {
  3672.                     switch (formatAs) {
  3673.                         case V1ToStringUnescape:
  3674.                            
  3675.                             mode = (InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape) | UnescapeMode.V1ToStringFlag;
  3676.                             if (IsImplicitFile)
  3677.                                 mode &= ~UnescapeMode.Unescape;
  3678.                             break;
  3679.                         case UriFormat.Unescaped:
  3680.                            
  3681.                             mode = IsImplicitFile ? UnescapeMode.CopyOnly : UnescapeMode.Unescape | UnescapeMode.UnescapeAll;
  3682.                             break;
  3683.                         default:
  3684.                            
  3685.                             // UriFormat.SafeUnescaped
  3686.                             mode = InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape;
  3687.                             if (IsImplicitFile)
  3688.                                 mode &= ~UnescapeMode.Unescape;
  3689.                             break;
  3690.                     }
  3691.                 }
  3692.                 else {
  3693.                     mode = UnescapeMode.CopyOnly;
  3694.                 }
  3695.                
  3696.                 char[] dest1 = new char[dest.Length];
  3697.                 Buffer.BlockCopy(dest, 0, dest1, 0, end << 1);
  3698.                 fixed (char* pdest = dest1) {
  3699.                     dest = UnescapeString(pdest, pos, end, dest, ref pos, '?', '#', c_DummyChar, mode);
  3700.                 }
  3701.             }
  3702.             else {
  3703.                 pos = end;
  3704.             }
  3705.            
  3706.             return dest;
  3707.         }
  3708.        
  3709.         // works only with ASCII characters, used to partially unescape path before compressing
  3710.         unsafe private static void UnescapeOnly(char* pch, int start, ref int end, char ch1, char ch2, char ch3)
  3711.         {
  3712.             if (end - start < 3) {
  3713.                 //no chance that something is escaped
  3714.                 return;
  3715.             }
  3716.            
  3717.             char* pend = pch + end - 2;
  3718.             pch += start;
  3719.             char* pnew = null;
  3720.             over:
  3721.            
  3722.            
  3723.             // Just looking for a interested escaped char
  3724.             if (pch >= pend)
  3725.                 goto done;
  3726.             if (*pch++ != '%')
  3727.                 goto over;
  3728.            
  3729.             char ch = EscapedAscii(*pch++, *pch++);
  3730.             if (!(ch == ch1 || ch == ch2 || ch == ch3))
  3731.                 goto over;
  3732.            
  3733.             // Here we found something and now start copying the scanned chars
  3734.             pnew = pch - 2;
  3735.             *(pnew - 1) = ch;
  3736.             over_new:
  3737.            
  3738.            
  3739.             if (pch >= pend)
  3740.                 goto done;
  3741.             if ((*pnew++ = *pch++) != '%')
  3742.                 goto over_new;
  3743.            
  3744.             ch = EscapedAscii((*pnew++ = *pch++), (*pnew++ = *pch++));
  3745.             if (!(ch == ch1 || ch == ch2 || ch == ch3)) {
  3746.                 goto over_new;
  3747.             }
  3748.            
  3749.             pnew -= 2;
  3750.             *(pnew - 1) = ch;
  3751.            
  3752.             goto over_new;
  3753.             done:
  3754.            
  3755.             pend += 2;
  3756.            
  3757.             if (pnew == null) {
  3758.                 //nothing was found
  3759.                 return;
  3760.             }
  3761.            
  3762.             //the tail may be already processed
  3763.             if (pch == pend) {
  3764.                 end -= (int)(pch - pnew);
  3765.                 return;
  3766.             }
  3767.            
  3768.             *pnew++ = *pch++;
  3769.             if (pch == pend) {
  3770.                 end -= (int)(pch - pnew);
  3771.                 return;
  3772.             }
  3773.             *pnew++ = *pch++;
  3774.             end -= (int)(pch - pnew);
  3775.         }
  3776.         //
  3777.         //
  3778.         //
  3779.         //
  3780.         //
  3781.         private static char EscapedAscii(char digit, char next)
  3782.         {
  3783.             if (!(((digit >= '0') && (digit <= '9')) || ((digit >= 'A') && (digit <= 'F')) || ((digit >= 'a') && (digit <= 'f')))) {
  3784.                 return c_DummyChar;
  3785.             }
  3786.            
  3787.             int res = (digit <= '9') ? ((int)digit - (int)'0') : (((digit <= 'F') ? ((int)digit - (int)'A') : ((int)digit - (int)'a')) + 10);
  3788.            
  3789.             if (!(((next >= '0') && (next <= '9')) || ((next >= 'A') && (next <= 'F')) || ((next >= 'a') && (next <= 'f')))) {
  3790.                 return c_DummyChar;
  3791.             }
  3792.            
  3793.             return (char)((res << 4) + ((next <= '9') ? ((int)next - (int)'0') : (((next <= 'F') ? ((int)next - (int)'A') : ((int)next - (int)'a')) + 10)));
  3794.         }
  3795.        
  3796.        
  3797.         //
  3798.         //
  3799.         // This will compress any "\" "/../" "/./" "///" "/..../" /XXX.../, etc found in the input
  3800.         //
  3801.         // The passed syntax controls whether to use agressive compression or the one specified in RFC 2396
  3802.         //
  3803.         //
  3804.         private static char[] Compress(char[] dest, ushort start, ref int destLength, UriParser syntax)
  3805.         {
  3806.             ushort slashCount = 0;
  3807.             ushort lastSlash = 0;
  3808.             ushort dotCount = 0;
  3809.             ushort removeSegments = 0;
  3810.            
  3811.             unchecked {
  3812.                 //ushort i == -1 and start == -1 overflow is ok here
  3813.                 ushort i = (ushort)((ushort)destLength - (ushort)1);
  3814.                 start = (ushort)(start - 1);
  3815.                
  3816.                 for (; i != start; --i) {
  3817.                     char ch = dest[i];
  3818.                     if (ch == '\\' && syntax.InFact(UriSyntaxFlags.ConvertPathSlashes)) {
  3819.                         dest[i] = ch = '/';
  3820.                     }
  3821.                    
  3822.                     //
  3823.                     // compress multiple '/' for file URI
  3824.                     //
  3825.                     if (ch == '/') {
  3826.                         ++slashCount;
  3827.                         if (slashCount > 1) {
  3828.                             continue;
  3829.                         }
  3830.                     }
  3831.                     else {
  3832.                         if (slashCount > 1) {
  3833.                             if (syntax.InFact(UriSyntaxFlags.CanonicalizeAsFilePath)) {
  3834.                                 // We saw > 1 slashes so remove all but the last one
  3835.                                 // dest.Remove(i+1, slashCount -1);
  3836.                                 Buffer.BlockCopy(dest, (i + slashCount) << 1, dest, (i + 1) << 1, (destLength - (i + slashCount)) << 1);
  3837.                                 destLength -= (slashCount - 1);
  3838.                             }
  3839.                             // else preserve repeated slashes
  3840.                             lastSlash = (ushort)(i + 1);
  3841.                         }
  3842.                         slashCount = 0;
  3843.                     }
  3844.                    
  3845.                     if (ch == '.') {
  3846.                         ++dotCount;
  3847.                         continue;
  3848.                     }
  3849.                     else if (dotCount != 0) {
  3850.                        
  3851.                         bool skipSegment = syntax.NotAny(UriSyntaxFlags.CanonicalizeAsFilePath) && (dotCount > 2 || ch != '/' || i == start);
  3852.                        
  3853.                         //
  3854.                         // Cases:
  3855.                         // /./ or /...[....]/ = remove this segment as invalid
  3856.                         // /../ = remove this segment, mark next for removal
  3857.                         // /....x = DO NOT TOUCH, leave as is
  3858.                         // x.../ = remove trailing dots
  3859.                         //
  3860.                         if (!skipSegment && ch == '/') {
  3861.                             if (lastSlash == i + dotCount + 1 || (lastSlash == 0 && i + dotCount + 1 == destLength)) {
  3862.                                 //
  3863.                                 // /./ or /...[....]/ or /.<eos> or /...[....]<eos>
  3864.                                 // Remove this segment (note that /.../ is an invalid segment, remove it since
  3865.                                 // we should not throw at this parsing point.
  3866.                                 //
  3867.                                 // just reusing a variable slot we perform //dest.Remove(i+1, dotCount + (lastSlash==0?0:1));
  3868.                                 lastSlash = (ushort)(i + 1 + dotCount + (lastSlash == 0 ? 0 : 1));
  3869.                                 Buffer.BlockCopy(dest, lastSlash << 1, dest, (i + 1) << 1, (destLength - lastSlash) << 1);
  3870.                                 destLength -= (lastSlash - i - 1);
  3871.                                
  3872.                                 lastSlash = i;
  3873.                                 if (dotCount == 2) {
  3874.                                     //
  3875.                                     // We have 2 dots in between like /../ or /..<eos>,
  3876.                                     // Mark next segment for removal and remove this /../ or /..
  3877.                                     //
  3878.                                     ++removeSegments;
  3879.                                 }
  3880.                                 dotCount = 0;
  3881.                                 continue;
  3882.                             }
  3883.                         }
  3884.                         // Note if removeSegments!=0, then ignore and remove the whole segment later
  3885.                         else if (!skipSegment && removeSegments == 0 && (lastSlash == i + dotCount + 1 || (lastSlash == 0 && i + dotCount + 1 == destLength))) {
  3886.                             //
  3887.                             // x.../ or x...<eos>
  3888.                             // remove trailing dots
  3889.                             //
  3890.                             //
  3891.                             // just reusing a variable slot we perform //dest.Remove(i+1, dotCount);
  3892.                             dotCount = (ushort)(i + 1 + dotCount);
  3893.                             Buffer.BlockCopy(dest, dotCount << 1, dest, (i + 1) << 1, (destLength - dotCount) << 1);
  3894.                             destLength -= (dotCount - i - 1);
  3895.                             lastSlash = 0;
  3896.                             //the other dots in this segment will stay intact
  3897.                             dotCount = 0;
  3898.                             continue;
  3899.                         }
  3900.                         dotCount = 0;
  3901.                         //
  3902.                         // Here all other cases go such as
  3903.                         // x.[..]y or /.[..]x or (/x.[...][/] && removeSegments !=0)
  3904.                     }
  3905.                    
  3906.                     //
  3907.                     // Now we may want to remove a segment because of previous /../
  3908.                     //
  3909.                     if (ch == '/') {
  3910.                         if (removeSegments != 0) {
  3911.                             --removeSegments;
  3912.                            
  3913.                             // just reusing a variable slot we perform //dest.Remove(i+1, lastSlash - i);
  3914.                             lastSlash = (ushort)(lastSlash + 1);
  3915.                             Buffer.BlockCopy(dest, lastSlash << 1, dest, (i + 1) << 1, (destLength - lastSlash) << 1);
  3916.                             destLength -= (lastSlash - i - 1);
  3917.                         }
  3918.                         lastSlash = i;
  3919.                     }
  3920.                 }
  3921.                
  3922.                 start = (ushort)((ushort)start + (ushort)1);
  3923.             }
  3924.             //end of unchecked
  3925.             if ((ushort)destLength > start && syntax.InFact(UriSyntaxFlags.CanonicalizeAsFilePath)) {
  3926.                 if (slashCount > 1) {
  3927.                     Buffer.BlockCopy(dest, lastSlash << 1, dest, start << 1, (destLength - lastSlash) << 1);
  3928.                     destLength -= (slashCount - 1);
  3929.                 }
  3930.                 else if (removeSegments != 0 && dest[start] != '/') {
  3931.                     //remove first not rooted segment
  3932.                     // dest.Remove(i+1, lastSlash - i);
  3933.                     lastSlash = (ushort)(lastSlash + 1);
  3934.                     Buffer.BlockCopy(dest, lastSlash << 1, dest, start << 1, (destLength - lastSlash) << 1);
  3935.                     destLength -= lastSlash;
  3936.                 }
  3937.                 else if (dotCount != 0) {
  3938.                     // If final string starts with a segment looking like .[...]/ or .[...]<eos>
  3939.                     // then we remove this fisrt segment
  3940.                     if (lastSlash == dotCount + 1 || (lastSlash == 0 && dotCount + 1 == destLength)) {
  3941.                         //dest.Remove(0, dotCount + (lastSlash==0?0:1));
  3942.                         dotCount = (ushort)(dotCount + (lastSlash == 0 ? 0 : 1));
  3943.                         Buffer.BlockCopy(dest, dotCount << 1, dest, start << 1, (destLength - dotCount) << 1);
  3944.                         destLength -= dotCount;
  3945.                     }
  3946.                 }
  3947.             }
  3948.             return dest;
  3949.         }
  3950.         //
  3951.         //
  3952.         //
  3953.         //
  3954.         private static readonly char[] HexUpperChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  3955.         'A', 'B', 'C', 'D', 'E', 'F'};
  3956.        
  3957.         //used by DigestClient
  3958.         static internal readonly char[] HexLowerChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  3959.         'a', 'b', 'c', 'd', 'e', 'f'};
  3960.        
  3961.        
  3962.         //
  3963.         //
  3964.         //
  3965.         //
  3966.         private static void EscapeAsciiChar(char ch, char[] to, ref int pos)
  3967.         {
  3968.             to[pos++] = '%';
  3969.             to[pos++] = HexUpperChars[(ch & 240) >> 4];
  3970.             to[pos++] = HexUpperChars[ch & 15];
  3971.         }
  3972.        
  3973.         static internal int CalculateCaseInsensitiveHashCode(string text)
  3974.         {
  3975.             return StringComparer.InvariantCultureIgnoreCase.GetHashCode(text);
  3976.         }
  3977.         //
  3978.         // CombineUri
  3979.         //
  3980.         // Given 2 URI strings, combine them into a single resultant URI string
  3981.         //
  3982.         // Inputs:
  3983.         // <argument> basePart
  3984.         // Base URI to combine with
  3985.         //
  3986.         // <argument> relativePart
  3987.         // String expected to be relative URI
  3988.         //
  3989.         // Assumes:
  3990.         // <basePart> is in canonic form
  3991.         //
  3992.         // Returns:
  3993.         // Resulting combined URI string
  3994.         //
  3995.         private static string CombineUri(Uri basePart, string relativePart, UriFormat uriFormat)
  3996.         {
  3997.             //NB: relativePart is ensured as not empty by the caller
  3998.             // Another assumption is that basePart is an AbsoluteUri
  3999.            
  4000.             // This method was not optimized for efficiency
  4001.             // Means a relative Uri ctor may be relatively slow plus it increases the footprint of the baseUri
  4002.            
  4003.             char c1 = relativePart[0];
  4004.            
  4005.             #if !PLATFORM_UNIX
  4006.             //check a special case for the base as DOS path and a rooted relative string
  4007.             if (basePart.IsDosPath && (c1 == '/' || c1 == '\\') && (relativePart.Length == 1 || (relativePart[1] != '/' && relativePart[1] != '\\'))) {
  4008.                 // take relative part appended to the base string after the drive letter
  4009.                 int idx = basePart.OriginalString.IndexOf(':');
  4010.                 if (basePart.IsImplicitFile) {
  4011.                     return basePart.OriginalString.Substring(0, idx + 1) + relativePart;
  4012.                 }
  4013.                 // The basePart has explicit scheme (could be not file:), take the DOS drive ':' position
  4014.                 idx = basePart.OriginalString.IndexOf(':', idx + 1);
  4015.                 return basePart.OriginalString.Substring(0, idx + 1) + relativePart;
  4016.             }
  4017.             #endif // !PLATFORM_UNIX
  4018.            
  4019.             // Check special case for Unc or absolute path in relativePart when base is FILE
  4020.             if (StaticIsFile(basePart.Syntax)) {
  4021.                
  4022.                 if (c1 == '\\' || c1 == '/') {
  4023.                    
  4024.                     if (relativePart.Length >= 2 && (relativePart[1] == '\\' || relativePart[1] == '/')) {
  4025.                         //Assuming relative is a Unc path and base is a file uri.
  4026.                         return basePart.IsImplicitFile ? relativePart : "file:" + relativePart;
  4027.                     }
  4028.                    
  4029.                     // here we got an absolute path in relativePart,
  4030.                     // For compatibility with V1.0 parser we restrict the compression scope to Unc Share, i.e. \\host\share\
  4031.                     if (basePart.IsUnc) {
  4032.                         string share = basePart.GetParts(UriComponents.Path | UriComponents.KeepDelimiter, UriFormat.Unescaped);
  4033.                         for (int i = 1; i < share.Length; ++i) {
  4034.                             if (share[i] == '/') {
  4035.                                 share = share.Substring(0, i);
  4036.                                 break;
  4037.                             }
  4038.                         }
  4039.                         if (basePart.IsImplicitFile) {
  4040.                             return "\\\\" + basePart.GetParts(UriComponents.Host, UriFormat.Unescaped) + share + relativePart;
  4041.                         }
  4042.                         return "file://" + basePart.GetParts(UriComponents.Host, uriFormat) + share + relativePart;
  4043.                        
  4044.                     }
  4045.                     // It's not obvious but we've checked (for this relativePart format) that baseUti is nor UNC nor DOS path
  4046.                     //
  4047.                     // Means base is a Unix style path and, btw, IsImplicitFile cannot be the case either
  4048.                     return "file://" + relativePart;
  4049.                 }
  4050.             }
  4051.            
  4052.             // If we are here we did not recognize absolute DOS/UNC path for a file: base uri
  4053.             // Note that DOS path may still happen in the relativePart and if so it may override the base uri scheme.
  4054.            
  4055.             bool convBackSlashes = basePart.Syntax.InFact(UriSyntaxFlags.ConvertPathSlashes);
  4056.            
  4057.             string left = null;
  4058.            
  4059.             // check for network or local absolute path
  4060.             if (c1 == '/' || (c1 == '\\' && convBackSlashes)) {
  4061.                 if (relativePart.Length >= 2 && relativePart[1] == '/') {
  4062.                     // got an authority in relative path and the base scheme is not file (checked)
  4063.                     return basePart.Scheme + ':' + relativePart;
  4064.                 }
  4065.                
  4066.                 // Got absolute relative path, and the base is nor FILE nor a DOS path (checked at the method start)
  4067.                 if (basePart.HostType == Flags.IPv6HostType) {
  4068.                     left = basePart.GetParts(UriComponents.Scheme | UriComponents.UserInfo, uriFormat) + '[' + basePart.DnsSafeHost + ']' + basePart.GetParts(UriComponents.KeepDelimiter | UriComponents.Port, uriFormat);
  4069.                 }
  4070.                 else {
  4071.                     left = basePart.GetParts(UriComponents.SchemeAndServer | UriComponents.UserInfo, uriFormat);
  4072.                 }
  4073.                 if (convBackSlashes && c1 == '\\')
  4074.                     relativePart = '/' + relativePart.Substring(1);
  4075.                
  4076.                 return left + relativePart;
  4077.             }
  4078.            
  4079.             // Here we got a relative path or just a query+[fragment]
  4080.             // Need to run path Compression because this is how relative Uri combining works
  4081.            
  4082.             // Take the base part path up to and including the last slash
  4083.             left = basePart.GetParts(UriComponents.Path | UriComponents.KeepDelimiter, basePart.IsImplicitFile ? UriFormat.Unescaped : uriFormat);
  4084.             int length = left.Length;
  4085.             char[] path = new char[length + relativePart.Length];
  4086.            
  4087.             if (length > 0) {
  4088.                 left.CopyTo(0, path, 0, length);
  4089.                 while (length > 0) {
  4090.                     if (path[--length] == '/') {
  4091.                         ++length;
  4092.                         break;
  4093.                     }
  4094.                 }
  4095.             }
  4096.            
  4097.             //Append relative path to the result
  4098.             relativePart.CopyTo(0, path, length, relativePart.Length);
  4099.            
  4100.             // Split relative on path and extra (for compression)
  4101.             c1 = basePart.Syntax.InFact(UriSyntaxFlags.MayHaveQuery) ? '?' : c_DummyChar;
  4102.            
  4103.             char c2 = (!basePart.IsImplicitFile && basePart.Syntax.InFact(UriSyntaxFlags.MayHaveFragment)) ? '#' : c_DummyChar;
  4104.             string extra = String.Empty;
  4105.            
  4106.             // assuming c_DummyChar may not happen in an unicode uri string
  4107.             if (!(c1 == c_DummyChar && c2 == c_DummyChar)) {
  4108.                 int i = 0;
  4109.                 for (; i < relativePart.Length; ++i) {
  4110.                     if (path[length + i] == c1 || path[length + i] == c2) {
  4111.                         break;
  4112.                     }
  4113.                 }
  4114.                 if (i == 0) {
  4115.                     extra = relativePart;
  4116.                 }
  4117.                 else if (i < relativePart.Length) {
  4118.                     extra = relativePart.Substring(i);
  4119.                 }
  4120.                 length += i;
  4121.             }
  4122.             else {
  4123.                 length += relativePart.Length;
  4124.             }
  4125.            
  4126.             // Take the base part up to the path
  4127.             if (basePart.HostType == Flags.IPv6HostType) {
  4128.                 if (basePart.IsImplicitFile) {
  4129.                     left = "\\\\[" + basePart.DnsSafeHost + ']';
  4130.                 }
  4131.                 else {
  4132.                     left = basePart.GetParts(UriComponents.Scheme | UriComponents.UserInfo, uriFormat) + '[' + basePart.DnsSafeHost + ']' + basePart.GetParts(UriComponents.KeepDelimiter | UriComponents.Port, uriFormat);
  4133.                 }
  4134.             }
  4135.             else {
  4136.                 if (basePart.IsImplicitFile) {
  4137.                     #if !PLATFORM_UNIX
  4138.                     if (basePart.IsDosPath) {
  4139.                         // The FILE DOS path comes as /c:/path, we have to exclude first 3 chars from compression
  4140.                         path = Compress(path, 3, ref length, basePart.Syntax);
  4141.                         return new string(path, 1, length - 1) + extra;
  4142.                     }
  4143.                     else {
  4144.                         left = "\\\\" + basePart.GetParts(UriComponents.Host, UriFormat.Unescaped);
  4145.                     }
  4146.                     #else
  4147.                     left = basePart.GetParts(UriComponents.Host, UriFormat.Unescaped);
  4148.                     #endif // !PLATFORM_UNIX
  4149.                    
  4150.                 }
  4151.                 else {
  4152.                     left = basePart.GetParts(UriComponents.SchemeAndServer | UriComponents.UserInfo, uriFormat);
  4153.                 }
  4154.             }
  4155.             //compress the path
  4156.             path = Compress(path, basePart.SecuredPathIndex, ref length, basePart.Syntax);
  4157.             return left + new string(path, 0, length) + extra;
  4158.         }
  4159.        
  4160.         //
  4161.         //
  4162.         //
  4163.         //
  4164.         //
  4165.         private static string PathDifference(string path1, string path2, bool compareCase)
  4166.         {
  4167.            
  4168.             int i;
  4169.             int si = -1;
  4170.            
  4171.             for (i = 0; (i < path1.Length) && (i < path2.Length); ++i) {
  4172.                 if ((path1[i] != path2[i]) && (compareCase || (Char.ToLower(path1[i], CultureInfo.InvariantCulture) != Char.ToLower(path2[i], CultureInfo.InvariantCulture)))) {
  4173.                     break;
  4174.                    
  4175.                 }
  4176.                 else if (path1[i] == '/') {
  4177.                     si = i;
  4178.                 }
  4179.             }
  4180.            
  4181.             if (i == 0) {
  4182.                 return path2;
  4183.             }
  4184.             if ((i == path1.Length) && (i == path2.Length)) {
  4185.                 return String.Empty;
  4186.             }
  4187.            
  4188.             StringBuilder relPath = new StringBuilder();
  4189.            
  4190.             for (; i < path1.Length; ++i) {
  4191.                 if (path1[i] == '/') {
  4192.                     relPath.Append("../");
  4193.                 }
  4194.             }
  4195.             return relPath.ToString() + path2.Substring(si + 1);
  4196.         }
  4197.        
  4198.         //Used by Uribuilder
  4199.         internal bool HasAuthority {
  4200.             get { return InFact(Flags.AuthorityFound); }
  4201.         }
  4202.        
  4203.         private static readonly char[] _WSchars = new char[] {' ', '\n', '\r', '\t'};
  4204.         private static bool IsLWS(char ch)
  4205.         {
  4206.            
  4207.             return (ch <= ' ') && (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t');
  4208.         }
  4209.        
  4210.         private static bool IsAsciiLetter(char character)
  4211.         {
  4212.            
  4213.             return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z');
  4214.         }
  4215.        
  4216.         private static bool IsAsciiLetterOrDigit(char character)
  4217.         {
  4218.             return IsAsciiLetter(character) || (character >= '0' && character <= '9');
  4219.         }
  4220.        
  4221.         /// <internalonly/>
  4222.         [Obsolete("The method has been deprecated. It is not used by the system. http://go.microsoft.com/fwlink/?linkid=14202")]
  4223.         protected virtual void Parse()
  4224.         {
  4225.         }
  4226.         /// <internalonly/>
  4227.         [Obsolete("The method has been deprecated. It is not used by the system. http://go.microsoft.com/fwlink/?linkid=14202")]
  4228.         protected virtual void Canonicalize()
  4229.         {
  4230.         }
  4231.         /// <internalonly/>
  4232.         [Obsolete("The method has been deprecated. It is not used by the system. http://go.microsoft.com/fwlink/?linkid=14202")]
  4233.         protected virtual void Escape()
  4234.         {
  4235.         }
  4236.         //
  4237.         // Unescape
  4238.         //
  4239.         // Convert any escape sequences in <path>. Escape sequences can be
  4240.         // hex encoded reserved characters (e.g. %40 == '@') or hex encoded
  4241.         // UTF-8 sequences (e.g. %C4%D2 == 'Latin capital Ligature Ij')
  4242.         //
  4243.         /// <internalonly/>
  4244.         [Obsolete("The method has been deprecated. Please use GetComponents() or static UnescapeDataString() to unescape a Uri component or a string. http://go.microsoft.com/fwlink/?linkid=14202")]
  4245.         protected virtual string Unescape(string path)
  4246.         {
  4247.            
  4248.             // alexeiv cr: This method is dangerous since it gives path unescaping control
  4249.             // to the derived class without any permission demand.
  4250.             // Should be deprecated and removed asap.
  4251.            
  4252.             char[] dest = new char[path.Length];
  4253.             int count = 0;
  4254.             dest = UnescapeString(path, 0, path.Length, dest, ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.Unescape | UnescapeMode.UnescapeAll);
  4255.             return new string(dest, 0, count);
  4256.         }
  4257.        
  4258.         [Obsolete("The method has been deprecated. Please use GetComponents() or static EscapeUriString() to escape a Uri component or a string. http://go.microsoft.com/fwlink/?linkid=14202")]
  4259.         protected static string EscapeString(string str)
  4260.         {
  4261.            
  4262.             // alexeiv cr: This method just does not make sense sa protected
  4263.             // It should go public static asap
  4264.            
  4265.             if ((object)str == null) {
  4266.                 return string.Empty;
  4267.             }
  4268.            
  4269.             int destStart = 0;
  4270.             char[] dest = EscapeString(str, 0, str.Length, null, ref destStart, true, '?', '#', '%');
  4271.             if ((object)dest == null)
  4272.                 return str;
  4273.             return new string(dest, 0, destStart);
  4274.         }
  4275.        
  4276.         //
  4277.         // CheckSecurity
  4278.         //
  4279.         // Check for any invalid or problematic character sequences
  4280.         //
  4281.         /// <internalonly/>
  4282.         [Obsolete("The method has been deprecated. It is not used by the system. http://go.microsoft.com/fwlink/?linkid=14202")]
  4283.         protected virtual void CheckSecurity()
  4284.         {
  4285.            
  4286.             // alexeiv cr: This method just does not make sense
  4287.             // Should be deprecated and removed asap.
  4288.            
  4289.             if (Scheme == "telnet") {
  4290.                
  4291.                 //
  4292.                 // remove everything after ';' for telnet
  4293.                 //
  4294.                
  4295.             }
  4296.         }
  4297.        
  4298.         //
  4299.         // IsReservedCharacter
  4300.         //
  4301.         // Determine whether a character is part of the reserved set
  4302.         //
  4303.         // Returns:
  4304.         // true if <character> is reserved else false
  4305.         //
  4306.         /// <internalonly/>
  4307.         [Obsolete("The method has been deprecated. It is not used by the system. http://go.microsoft.com/fwlink/?linkid=14202")]
  4308.         protected virtual bool IsReservedCharacter(char character)
  4309.         {
  4310.            
  4311.             // alexeiv cr: This method just does not make sense as virtual protected
  4312.             // It should go public static asap
  4313.            
  4314.                 // OK FS char
  4315.                 // OK FS char
  4316.                 // OK FS char
  4317.             return (character == ';') || (character == '/') || (character == ':') || (character == '@') || (character == '&') || (character == '=') || (character == '+') || (character == '$') || (character == ',');
  4318.         }
  4319.        
  4320.         //
  4321.         // IsExcludedCharacter
  4322.         //
  4323.         // Determine if a character should be exluded from a URI and therefore be
  4324.         // escaped
  4325.         //
  4326.         // Returns:
  4327.         // true if <character> should be escaped else false
  4328.         //
  4329.         /// <internalonly/>
  4330.         [Obsolete("The method has been deprecated. It is not used by the system. http://go.microsoft.com/fwlink/?linkid=14202")]
  4331.         protected static bool IsExcludedCharacter(char character)
  4332.         {
  4333.            
  4334.             // alexeiv cr: This method just does not make sense sa protected
  4335.             // It should go public static asap
  4336.            
  4337.             //
  4338.             // the excluded characters...
  4339.             //
  4340.            
  4341.                
  4342.                 //
  4343.                 // the 'unwise' characters...
  4344.                 //
  4345.                
  4346.             return (character <= 32) || (character >= 127) || (character == '<') || (character == '>') || (character == '#') || (character == '%') || (character == '"') || (character == '{') || (character == '}') || (character == '|') || (character == '\\') || (character == '^') || (character == '[') || (character == ']') || (character == '`');
  4347.         }
  4348.        
  4349.         //
  4350.         // IsBadFileSystemCharacter
  4351.         //
  4352.         // Determine whether a character would be an invalid character if used in
  4353.         // a file system name. Note, this is really based on NTFS rules
  4354.         //
  4355.         // Returns:
  4356.         // true if <character> would be a treated as a bad file system character
  4357.         // else false
  4358.         //
  4359.         [Obsolete("The method has been deprecated. It is not used by the system. http://go.microsoft.com/fwlink/?linkid=14202")]
  4360.         protected virtual bool IsBadFileSystemCharacter(char character)
  4361.         {
  4362.            
  4363.             // alexeiv cr: This method just does not make sense sa protected virtual
  4364.             // It should go public static asap
  4365.            
  4366.             return (character < 32) || (character == ';') || (character == '/') || (character == '?') || (character == ':') || (character == '&') || (character == '=') || (character == ',') || (character == '*') || (character == '<') || (character == '>') || (character == '"') || (character == '|') || (character == '\\') || (character == '^');
  4367.         }
  4368.        
  4369.        
  4370.     }
  4371.     // class Uri
  4372. }
  4373. // namespace System

Developer Fusion