2 Copyright (c) 2003 Microsoft Corporation
10 Uri extensibility model Implementation.
11 This file utilizes partial class feature.
12 Uri.cs file contains core System.Uri functionality.
15 Alexei Vopilov Nov 21 2003
25 using System.Globalization;
27 using System.Runtime.InteropServices;
28 using System.Diagnostics;
30 public partial class Uri {
32 // All public ctors go through here
34 private void CreateThis(string uri, bool dontEscape, UriKind uriKind)
36 // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow
38 if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative) {
40 if (uriKind != DotNetRelativeOrAbsolute)
42 throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind));
45 m_String = uri == null? string.Empty: uri;
48 m_Flags |= Flags.UserEscaped;
50 ParsingError err = ParseScheme(m_String, ref m_Flags, ref m_Syntax);
53 InitializeUri(err, uriKind, out e);
58 private void InitializeUri(ParsingError err, UriKind uriKind, out UriFormatException e)
60 if (err == ParsingError.None)
65 if (uriKind == UriKind.RelativeOrAbsolute && m_String.Length > 0 && m_String[0] == '/' && !useDotNetRelativeOrAbsolute) {
66 // For original Mono behaviour where / is considered to be absolute path
70 // V1 compat VsWhidbey#252282
71 // A relative Uri wins over implicit UNC path unless the UNC path is of the form "\\something" and
72 // uriKind != Absolute
75 NotAny(Flags.DosPath) &&
76 #endif // !PLATFORM_UNIX
77 uriKind != UriKind.Absolute &&
78 (uriKind == UriKind.Relative || (m_String.Length >= 2 && (m_String[0] != '\\' || m_String[1] != '\\'))))
81 m_Syntax = null; //make it be relative Uri
82 m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
85 // Otheriwse an absolute file Uri wins when it's of the form "\\something"
88 // VsWhidbey#423805 and V1 compat issue
89 // We should support relative Uris of the form c:\bla or c:/bla
92 else if (uriKind == UriKind.Relative && InFact(Flags.DosPath))
94 m_Syntax = null; //make it be relative Uri
95 m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
98 // Otheriwse an absolute file Uri wins when it's of the form "c:\something"
100 #endif // !PLATFORM_UNIX
103 else if (err > ParsingError.LastRelativeUriOkErrIndex)
105 //This is a fatal error based solely on scheme name parsing
106 m_String = null; // make it be invalid Uri
107 e = GetException(err);
114 bool hasUnicode = false;
116 // Is there unicode ..
117 if ((!s_ConfigInitialized) && CheckForConfigLoad(m_String)){
118 InitializeUriConfig();
121 m_iriParsing = (s_IriParsing && ((m_Syntax == null) || m_Syntax.InFact(UriSyntaxFlags.AllowIriParsing)));
124 (CheckForUnicode(m_String) || CheckForEscapedUnreserved(m_String))) {
125 m_Flags |= Flags.HasUnicode;
127 // switch internal strings
128 m_originalUnicodeString = m_String; // original string location changed
131 if (m_Syntax != null)
133 if (m_Syntax.IsSimple)
135 if ((err = PrivateParseMinimal()) != ParsingError.None)
137 if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex)
139 // RFC 3986 Section 5.4.2 - http:(relativeUri) may be considered a valid relative Uri.
140 m_Syntax = null; // convert to relative uri
142 m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
145 e = GetException(err);
147 else if (uriKind == UriKind.Relative)
149 // Here we know that we can create an absolute Uri, but the user has requested only a relative one
150 e = GetException(ParsingError.CannotCreateRelative);
154 // will return from here
156 if (m_iriParsing && hasUnicode){
157 // In this scenario we need to parse the whole string
158 EnsureParseRemaining();
163 // offer custom parser to create a parsing context
164 m_Syntax = m_Syntax.InternalOnNewUri();
166 // incase they won't call us
167 m_Flags |= Flags.UserDrivenParsing;
169 // Ask a registered type to validate this uri
170 m_Syntax.InternalValidate(this, out e);
174 // Can we still take it as a relative Uri?
175 if (uriKind != UriKind.Absolute && err != ParsingError.None
176 && err <= ParsingError.LastRelativeUriOkErrIndex)
178 m_Syntax = null; // convert it to relative
180 m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
185 if (err != ParsingError.None || InFact(Flags.ErrorOrParsingRecursion))
187 // User parser took over on an invalid Uri
188 SetUserDrivenParsing();
190 else if (uriKind == UriKind.Relative)
192 // Here we know that custom parser can create an absolute Uri, but the user has requested only a
194 e = GetException(ParsingError.CannotCreateRelative);
197 if (m_iriParsing && hasUnicode){
198 // In this scenario we need to parse the whole string
199 EnsureParseRemaining();
203 // will return from here
206 // If we encountered any parsing errors that indicate this may be a relative Uri,
207 // and we'll allow relative Uri's, then create one.
208 else if (err != ParsingError.None && uriKind != UriKind.Absolute
209 && err <= ParsingError.LastRelativeUriOkErrIndex)
212 m_Flags &= (Flags.UserEscaped | Flags.HasUnicode); // the only flags that makes sense for a relative uri
213 if (m_iriParsing && hasUnicode)
215 // Iri'ze and then normalize relative uris
216 m_String = EscapeUnescapeIri(m_originalUnicodeString, 0, m_originalUnicodeString.Length,
220 if (UriParser.ShouldUseLegacyV2Quirks)
221 m_String = m_String.Normalize(NormalizationForm.FormC);
223 catch (ArgumentException)
225 e = GetException(ParsingError.BadFormat);
231 m_String = null; // make it be invalid Uri
232 e = GetException(err);
237 // Checks if there are any unicode or escaped chars or ace to determine whether to load
240 private unsafe bool CheckForConfigLoad(String data)
242 bool initConfig = false;
243 int length = data.Length;
245 fixed (char* temp = data){
246 for (int i = 0; i < length; ++i){
248 if ((temp[i] > '\x7f') || (temp[i] == '%') ||
249 ((temp[i] == 'x') && ((i + 3) < length) && (temp[i + 1] == 'n') && (temp[i + 2] == '-') && (temp[i + 3] == '-')))
252 // Unicode or maybe ace
265 // Unescapes entire string and checks if it has unicode chars
267 private bool CheckForUnicode(String data)
269 bool hasUnicode = false;
270 char[] chars = new char[data.Length];
273 chars = UriHelper.UnescapeString(data, 0, data.Length, chars, ref count, c_DummyChar, c_DummyChar,
274 c_DummyChar, UnescapeMode.Unescape | UnescapeMode.UnescapeAll, null, false);
276 for (int i = 0; i < count; ++i){
277 if (chars[i] > '\x7f'){
286 // Does this string have any %6A sequences that are 3986 Unreserved characters? These should be un-escaped.
287 private unsafe bool CheckForEscapedUnreserved(String data)
289 fixed (char* tempPtr = data)
291 for (int i = 0; i < data.Length - 2; ++i)
293 if (tempPtr[i] == '%' && IsHexDigit(tempPtr[i + 1]) && IsHexDigit(tempPtr[i + 2])
294 && tempPtr[i + 1] >= '0' && tempPtr[i + 1] <= '7') // max 0x7F
296 char ch = UriHelper.EscapedAscii(tempPtr[i + 1], tempPtr[i + 2]);
297 if (ch != c_DummyChar && UriHelper.Is3986Unreserved(ch))
309 // Returns true if the string represents a valid argument to the Uri ctor
310 // If uriKind != AbsoluteUri then certain parsing erros are ignored but Uri usage is limited
312 public static bool TryCreate(string uriString, UriKind uriKind, out Uri result)
314 if ((object)uriString == null)
319 UriFormatException e = null;
320 result = CreateHelper(uriString, false, uriKind, ref e);
321 return (object) e == null && result != null;
324 public static bool TryCreate(Uri baseUri, string relativeUri, out Uri result)
329 DotNetRelativeOrAbsolute;
333 if (TryCreate(relativeUri, kind, out relativeLink))
335 if (!relativeLink.IsAbsoluteUri)
336 return TryCreate(baseUri, relativeLink, out result);
338 result = relativeLink;
345 public static bool TryCreate(Uri baseUri, Uri relativeUri, out Uri result)
349 //Consider: Work out the baseUri==null case
350 if ((object)baseUri == null || (object)relativeUri == null)
353 if (baseUri.IsNotAbsoluteUri)
356 UriFormatException e;
357 string newUriString = null;
360 if (baseUri.Syntax.IsSimple)
362 dontEscape = relativeUri.UserEscaped;
363 result = ResolveHelper(baseUri, relativeUri, ref newUriString, ref dontEscape, out e);
368 newUriString = baseUri.Syntax.InternalResolve(baseUri, relativeUri, out e);
374 if ((object) result == null)
375 result = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e);
377 return (object) e == null && result != null && result.IsAbsoluteUri;
381 public string GetComponents(UriComponents components, UriFormat format)
383 if (((components & UriComponents.SerializationInfoString) != 0) && components != UriComponents.SerializationInfoString)
384 throw new ArgumentOutOfRangeException("components", components, SR.GetString(SR.net_uri_NotJustSerialization));
386 if ((format & ~UriFormat.SafeUnescaped) != 0)
387 throw new ArgumentOutOfRangeException("format");
389 if (IsNotAbsoluteUri)
391 if (components == UriComponents.SerializationInfoString)
392 return GetRelativeSerializationString(format);
394 throw new InvalidOperationException(SR.GetString(SR.net_uri_NotAbsolute));
398 return GetComponentsHelper(components, format);
400 return Syntax.InternalGetComponents(this, components, format);
404 // This is for languages that do not support == != operators overloading
406 // Note that Uri.Equals will get an optimized path but is limited to true/fasle result only
408 public static int Compare(Uri uri1, Uri uri2, UriComponents partsToCompare, UriFormat compareFormat,
409 StringComparison comparisonType)
412 if ((object) uri1 == null)
416 return -1; // null < non-null
418 if ((object) uri2 == null)
419 return 1; // non-null > null
421 // a relative uri is always less than an absolute one
422 if (!uri1.IsAbsoluteUri || !uri2.IsAbsoluteUri)
423 return uri1.IsAbsoluteUri? 1: uri2.IsAbsoluteUri? -1: string.Compare(uri1.OriginalString,
424 uri2.OriginalString, comparisonType);
426 return string.Compare(
427 uri1.GetParts(partsToCompare, compareFormat),
428 uri2.GetParts(partsToCompare, compareFormat),
433 public bool IsWellFormedOriginalString()
435 if (IsNotAbsoluteUri || Syntax.IsSimple)
436 return InternalIsWellFormedOriginalString();
438 return Syntax.InternalIsWellFormedOriginalString(this);
441 // Consider: (perf) Making it to not create a Uri internally
442 public static bool IsWellFormedUriString(string uriString, UriKind uriKind)
447 if (uriKind == UriKind.RelativeOrAbsolute)
448 uriKind = DotNetRelativeOrAbsolute;
451 if (!Uri.TryCreate(uriString, uriKind, out result))
454 return result.IsWellFormedOriginalString();
461 // Returns false if OriginalString value
462 // (1) is not correctly escaped as per URI spec excluding intl UNC name case
463 // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file"
464 // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file"
465 // (4) or contains unescaped backslashes even if they will be treated
466 // as forward slashes like http:\\host/path\file or file:\\\c:\path
468 internal unsafe bool InternalIsWellFormedOriginalString()
470 if (UserDrivenParsing)
471 throw new InvalidOperationException(SR.GetString(SR.net_uri_UserDrivenParsing, this.GetType().FullName));
473 fixed (char* str = m_String)
477 // For a relative Uri we only care about escaping and backslashes
481 // my:scheme/path?query is not well formed because the colon is ambiguous
482 if (!UriParser.ShouldUseLegacyV2Quirks && CheckForColonInFirstPathSegment(m_String))
486 return (CheckCanonical(str, ref idx, (ushort)m_String.Length, c_EOL)
487 & (Check.BackslashInPath | Check.EscapedCanonical)) == Check.EscapedCanonical;
491 // (2) or is an absolute Uri that represents implicit file Uri "c:\dir\file"
496 //This will get all the offsets, a Host name will be checked separatelly below
497 EnsureParseRemaining();
499 Flags nonCanonical = (m_Flags & (Flags.E_CannotDisplayCanonical | Flags.IriCanonical));
500 // User, Path, Query or Fragment may have some non escaped characters
501 if (((nonCanonical & Flags.E_CannotDisplayCanonical & (Flags.E_UserNotCanonical | Flags.E_PathNotCanonical |
502 Flags.E_QueryNotCanonical | Flags.E_FragmentNotCanonical)) != Flags.Zero) &&
503 (!m_iriParsing || (m_iriParsing &&
504 (((nonCanonical & Flags.E_UserNotCanonical) == 0) || ((nonCanonical & Flags.UserIriCanonical) == 0)) &&
505 (((nonCanonical & Flags.E_PathNotCanonical) == 0) || ((nonCanonical & Flags.PathIriCanonical) == 0)) &&
506 (((nonCanonical & Flags.E_QueryNotCanonical) == 0) || ((nonCanonical & Flags.QueryIriCanonical) == 0)) &&
507 (((nonCanonical & Flags.E_FragmentNotCanonical) == 0) || ((nonCanonical & Flags.FragmentIriCanonical) == 0)))))
512 // checking on scheme:\\ or file:////
513 if (InFact(Flags.AuthorityFound))
515 idx = (ushort)(m_Info.Offset.Scheme + m_Syntax.SchemeName.Length + 2);
516 if (idx >= m_Info.Offset.User || m_String[idx - 1] == '\\' || m_String[idx] == '\\')
520 if (InFact(Flags.UncPath | Flags.DosPath))
522 while (++idx < m_Info.Offset.User && (m_String[idx] == '/' || m_String[idx] == '\\'))
525 #endif // !PLATFORM_UNIX
529 // (3) or is an absolute Uri that misses a slash before path "file://c:/dir/file"
530 // Note that for this check to be more general we assert that if Path is non empty and if it requires a first slash
531 // (which looks absent) then the method has to fail.
532 // Today it's only possible for a Dos like path, i.e. file://c:/bla would fail below check.
533 if (InFact(Flags.FirstSlashAbsent) && m_Info.Offset.Query > m_Info.Offset.Path)
536 // (4) or contains unescaped backslashes even if they will be treated
537 // as forward slashes like http:\\host/path\file or file:\\\c:\path
538 // Note we do not check for Flags.ShouldBeCompressed i.e. allow // /./ and alike as valid
539 if (InFact(Flags.BackslashInPath))
542 // Capturing a rare case like file:///c|/dir
543 if (IsDosPath && m_String[m_Info.Offset.Path + SecuredPathIndex - 1] == '|')
547 // May need some real CPU processing to anwser the request
550 // Check escaping for authority
552 // IPv6 hosts cannot be properly validated by CheckCannonical
553 if ((m_Flags & Flags.CanonicalDnsHost) == 0 && HostType != Flags.IPv6HostType)
555 idx = m_Info.Offset.User;
556 Check result = CheckCanonical(str, ref idx, (ushort)m_Info.Offset.Path, '/');
557 if (((result & (Check.ReservedFound | Check.BackslashInPath | Check.EscapedCanonical))
558 != Check.EscapedCanonical)
559 && (!m_iriParsing || (m_iriParsing
560 && ((result & (Check.DisplayCanonical | Check.FoundNonAscii | Check.NotIriCanonical))
561 != (Check.DisplayCanonical | Check.FoundNonAscii)))))
567 // Want to ensure there are slashes after the scheme
568 if ((m_Flags & (Flags.SchemeNotCanonical | Flags.AuthorityFound))
569 == (Flags.SchemeNotCanonical | Flags.AuthorityFound))
571 idx = (ushort)m_Syntax.SchemeName.Length;
572 while (str[idx++] != ':') ;
573 if (idx + 1 >= m_String.Length || str[idx] != '/' || str[idx + 1] != '/')
578 // May be scheme, host, port or path need some canonicalization but still the uri string is found to be a
587 public static string UnescapeDataString(string stringToUnescape)
589 if ((object) stringToUnescape == null)
590 throw new ArgumentNullException("stringToUnescape");
592 if (stringToUnescape.Length == 0)
596 fixed (char* pStr = stringToUnescape)
599 for (position = 0; position < stringToUnescape.Length; ++position)
600 if (pStr[position] == '%')
603 if (position == stringToUnescape.Length)
604 return stringToUnescape;
606 UnescapeMode unescapeMode = UnescapeMode.Unescape | UnescapeMode.UnescapeAll;
608 char[] dest = new char[stringToUnescape.Length];
609 dest = UriHelper.UnescapeString(stringToUnescape, 0, stringToUnescape.Length, dest, ref position,
610 c_DummyChar, c_DummyChar, c_DummyChar, unescapeMode, null, false);
611 return new string(dest, 0, position);
616 // Where stringToEscape is intented to be a completely unescaped URI string.
617 // This method will escape any character that is not a reserved or unreserved character, including percent signs.
618 // Note that EscapeUriString will also do not escape a '#' sign.
620 public static string EscapeUriString(string stringToEscape)
622 if ((object)stringToEscape == null)
623 throw new ArgumentNullException("stringToEscape");
625 if (stringToEscape.Length == 0)
629 char[] dest = UriHelper.EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, true,
630 c_DummyChar, c_DummyChar, c_DummyChar);
631 if ((object) dest == null)
632 return stringToEscape;
633 return new string(dest, 0, position);
636 // Where stringToEscape is intended to be URI data, but not an entire URI.
637 // This method will escape any character that is not an unreserved character, including percent signs.
639 public static string EscapeDataString(string stringToEscape)
641 if ((object) stringToEscape == null)
642 throw new ArgumentNullException("stringToEscape");
644 if (stringToEscape.Length == 0)
648 char[] dest = UriHelper.EscapeString(stringToEscape, 0, stringToEscape.Length, null, ref position, false,
649 c_DummyChar, c_DummyChar, c_DummyChar);
651 return stringToEscape;
652 return new string(dest, 0, position);
656 // Cleans up the specified component according to Iri rules
657 // a) Chars allowed by iri in a component are unescaped if found escaped
658 // b) Bidi chars are stripped
660 // should be called only if IRI parsing is switched on
661 internal unsafe string EscapeUnescapeIri(string input, int start, int end, UriComponents component)
663 fixed (char *pInput = input)
665 return IriHelper.EscapeUnescapeIri(pInput, start, end, component);
669 // Should never be used except by the below method
670 private Uri(Flags flags, UriParser uriParser, string uri)
673 m_Syntax = uriParser;
677 // a Uri.TryCreate() method goes through here.
679 internal static Uri CreateHelper(string uriString, bool dontEscape, UriKind uriKind, ref UriFormatException e)
681 // if (!Enum.IsDefined(typeof(UriKind), uriKind)) -- We currently believe that Enum.IsDefined() is too slow
683 if ((int)uriKind < (int)UriKind.RelativeOrAbsolute || (int)uriKind > (int)UriKind.Relative){
685 if (uriKind != DotNetRelativeOrAbsolute)
687 throw new ArgumentException(SR.GetString(SR.net_uri_InvalidUriKind, uriKind));
690 UriParser syntax = null;
691 Flags flags = Flags.Zero;
692 ParsingError err = ParseScheme(uriString, ref flags, ref syntax);
695 flags |= Flags.UserEscaped;
697 // We won't use User factory for these errors
698 if (err != ParsingError.None)
700 // If it looks as a relative Uri, custom factory is ignored
701 if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex)
702 return new Uri((flags & Flags.UserEscaped), null, uriString);
707 // Cannot be relative Uri if came here
708 Uri result = new Uri(flags, syntax, uriString);
710 // Validate instance using ether built in or a user Parser
713 result.InitializeUri(err, uriKind, out e);
720 catch (UriFormatException ee)
722 Debug.Assert(!syntax.IsSimple, "A UriPraser threw on InitializeAndValidate.");
724 // A precaution since custom Parser should never throw in this case.
729 // Resolves into either baseUri or relativeUri according to conditions OR if not possible it uses newUriString
730 // to return combined URI strings from both Uris
731 // otherwise if e != null on output the operation has failed
734 internal static Uri ResolveHelper(Uri baseUri, Uri relativeUri, ref string newUriString, ref bool userEscaped,
735 out UriFormatException e)
737 Debug.Assert(!baseUri.IsNotAbsoluteUri && !baseUri.UserDrivenParsing, "Uri::ResolveHelper()|baseUri is not Absolute or is controlled by User Parser.");
740 string relativeStr = string.Empty;
742 if ((object)relativeUri != null)
744 if (relativeUri.IsAbsoluteUri) {
746 // Hack for Mono specific handling of /paths which are absolute on unix but
747 // we want to allow concatination of such paths to match .net
748 if (!(!IsWindowsFileSystem && relativeUri.OriginalString [0] == '/' && relativeUri.IsImplicitFile))
754 relativeStr = relativeUri.OriginalString;
755 userEscaped = relativeUri.UserEscaped;
758 relativeStr = string.Empty;
760 // Here we can assert that passed "relativeUri" is indeed a relative one
762 if (relativeStr.Length > 0 && (IsLWS(relativeStr[0]) || IsLWS(relativeStr[relativeStr.Length - 1])))
763 relativeStr = relativeStr.Trim(_WSchars);
765 if (relativeStr.Length == 0)
767 newUriString = baseUri.GetParts(UriComponents.AbsoluteUri,
768 baseUri.UserEscaped ? UriFormat.UriEscaped : UriFormat.SafeUnescaped);
772 // Check for a simple fragment in relative part
773 if (relativeStr[0] == '#' && !baseUri.IsImplicitFile && baseUri.Syntax.InFact(UriSyntaxFlags.MayHaveFragment))
775 newUriString = baseUri.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment,
776 UriFormat.UriEscaped) + relativeStr;
780 // Check for a simple query in relative part
781 if (relativeStr[0] == '?' && !baseUri.IsImplicitFile && baseUri.Syntax.InFact(UriSyntaxFlags.MayHaveQuery))
783 newUriString = baseUri.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Query & ~UriComponents.Fragment,
784 UriFormat.UriEscaped) + relativeStr;
788 // Check on the DOS path in the relative Uri (a special case)
789 if (relativeStr.Length >= 3
790 && (relativeStr[1] == ':' || relativeStr[1] == '|')
791 && IsAsciiLetter(relativeStr[0])
792 && (relativeStr[2] == '\\' || relativeStr[2] == '/'))
795 if (baseUri.IsImplicitFile)
797 // It could have file:/// prepended to the result but we want to keep it as *Implicit* File Uri
798 newUriString = relativeStr;
801 else if (baseUri.Syntax.InFact(UriSyntaxFlags.AllowDOSPath))
803 // The scheme is not changed just the path gets replaced
805 if (baseUri.InFact(Flags.AuthorityFound))
806 prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":///" : "://";
808 prefix = baseUri.Syntax.InFact(UriSyntaxFlags.PathIsRooted) ? ":/" : ":";
810 newUriString = baseUri.Scheme + prefix + relativeStr;
813 // If we are here then input like "http://host/path/" + "C:\x" will produce the result http://host/path/c:/x
817 ParsingError err = GetCombinedString(baseUri, relativeStr, userEscaped, ref newUriString);
819 if (err != ParsingError.None)
821 e = GetException(err);
825 if ((object)newUriString == (object)baseUri.m_String)
831 private unsafe string GetRelativeSerializationString(UriFormat format)
833 if (format == UriFormat.UriEscaped)
835 if (m_String.Length == 0)
838 char[] dest = UriHelper.EscapeString(m_String, 0, m_String.Length, null, ref position, true,
839 c_DummyChar, c_DummyChar, '%');
840 if ((object)dest == null)
842 return new string(dest, 0, position);
845 else if (format == UriFormat.Unescaped)
846 return UnescapeDataString(m_String);
848 else if (format == UriFormat.SafeUnescaped)
850 if (m_String.Length == 0)
853 char[] dest = new char[m_String.Length];
855 dest = UriHelper.UnescapeString(m_String, 0, m_String.Length, dest, ref position, c_DummyChar,
856 c_DummyChar, c_DummyChar, UnescapeMode.EscapeUnescape, null, false);
857 return new string(dest, 0, position);
860 throw new ArgumentOutOfRangeException("format");
865 // UriParser helpers methods
867 internal string GetComponentsHelper(UriComponents uriComponents, UriFormat uriFormat)
869 if (uriComponents == UriComponents.Scheme)
870 return m_Syntax.SchemeName;
872 // A serialzation info is "almost" the same as AbsoluteUri except for IPv6 + ScopeID hostname case
873 if ((uriComponents & UriComponents.SerializationInfoString) != 0)
874 uriComponents |= UriComponents.AbsoluteUri;
876 //This will get all the offsets, HostString will be created below if needed
877 EnsureParseRemaining();
879 if ((uriComponents & UriComponents.NormalizedHost) != 0)
881 // Down the path we rely on Host to be ON for NormalizedHost
882 uriComponents |= UriComponents.Host;
885 //Check to see if we need the host/authotity string
886 if ((uriComponents & UriComponents.Host) != 0)
887 EnsureHostString(true);
889 //This, single Port request is always processed here
890 if (uriComponents == UriComponents.Port || uriComponents == UriComponents.StrongPort)
892 if (((m_Flags & Flags.NotDefaultPort) != 0) || (uriComponents == UriComponents.StrongPort
893 && m_Syntax.DefaultPort != UriParser.NoDefaultPort))
895 // recreate string from the port value
896 return m_Info.Offset.PortValue.ToString(CultureInfo.InvariantCulture);
901 if ((uriComponents & UriComponents.StrongPort) != 0)
903 // Down the path we rely on Port to be ON for StrongPort
904 uriComponents |= UriComponents.Port;
907 //This request sometime is faster to process here
908 if (uriComponents == UriComponents.Host && (uriFormat == UriFormat.UriEscaped
909 || (( m_Flags & (Flags.HostNotCanonical | Flags.E_HostNotCanonical)) == 0)))
911 EnsureHostString(false);
917 case UriFormat.UriEscaped:
918 return GetEscapedParts(uriComponents);
920 case V1ToStringUnescape:
921 case UriFormat.SafeUnescaped:
922 case UriFormat.Unescaped:
923 return GetUnescapedParts(uriComponents, uriFormat);
926 throw new ArgumentOutOfRangeException("uriFormat");
931 public bool IsBaseOf(Uri uri)
933 if ((object)uri == null)
934 throw new ArgumentNullException("uri");
940 return IsBaseOfHelper(uri);
942 return Syntax.InternalIsBaseOf(this, uri);
947 internal bool IsBaseOfHelper(Uri uriLink)
950 if (!IsAbsoluteUri || UserDrivenParsing)
953 if (!uriLink.IsAbsoluteUri)
955 //a relative uri could have quite tricky form, it's better to fix it now.
956 string newUriString = null;
957 UriFormatException e;
958 bool dontEscape = false;
960 uriLink = ResolveHelper(this, uriLink, ref newUriString, ref dontEscape, out e);
964 if ((object)uriLink == null)
965 uriLink = CreateHelper(newUriString, dontEscape, UriKind.Absolute, ref e);
971 if (Syntax.SchemeName != uriLink.Syntax.SchemeName)
974 // Canonicalize and test for substring match up to the last path slash
975 string me = GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped);
976 string she = uriLink.GetParts(UriComponents.AbsoluteUri & ~UriComponents.Fragment, UriFormat.SafeUnescaped);
980 fixed (char* pMe = me)
982 fixed (char* pShe = she)
984 return UriHelper.TestForSubPath(pMe, (ushort)me.Length, pShe, (ushort)she.Length,
985 IsUncOrDosPath || uriLink.IsUncOrDosPath);
991 // Only a ctor time call
993 private void CreateThisFromUri(Uri otherUri)
995 // Clone the other guy but develop own UriInfo member
998 m_Flags = otherUri.m_Flags;
999 if (InFact(Flags.MinimalUriInfoSet))
1001 m_Flags &= ~(Flags.MinimalUriInfoSet | Flags.AllUriInfoSet | Flags.IndexMask);
1002 // Port / Path offset
1003 int portIndex = otherUri.m_Info.Offset.Path;
1004 if (InFact(Flags.NotDefaultPort))
1006 // Find the start of the port. Account for non-canonical ports like :00123
1007 while (otherUri.m_String[portIndex] != ':' && portIndex > otherUri.m_Info.Offset.Host)
1011 if (otherUri.m_String[portIndex] != ':')
1013 // Something wrong with the NotDefaultPort flag. Reset to path index
1014 Debug.Assert(false, "Uri failed to locate custom port at index: " + portIndex);
1015 portIndex = otherUri.m_Info.Offset.Path;
1018 m_Flags |= (Flags)portIndex; // Port or path
1021 m_Syntax = otherUri.m_Syntax;
1022 m_String = otherUri.m_String;
1023 m_iriParsing = otherUri.m_iriParsing;
1024 if (otherUri.OriginalStringSwitched){
1025 m_originalUnicodeString = otherUri.m_originalUnicodeString;
1027 if (otherUri.AllowIdn && (otherUri.InFact(Flags.IdnHost) || otherUri.InFact(Flags.UnicodeHost))){
1028 m_DnsSafeHost = otherUri.m_DnsSafeHost;