private string ReCreateParts(UriComponents parts, ushort nonCanonical, UriFormat formatAs)
{
EnsureHostString(false);
string stemp = (parts & UriComponents.Host) == 0 ? string.Empty : _info.Host;
// we reserve more space than required because a canonical Ipv6 Host
// may take more characters than in original m_String
// Also +3 is for :// and +1 is for absent first slash
// Also we may escape every character, hence multiplying by 12
// UTF-8 can use up to 4 bytes per char * 3 chars per byte (%A4) = 12 encoded chars
int count = (_info.Offset.End - _info.Offset.User) * (formatAs == UriFormat.UriEscaped ? 12 : 1);
char[] chars = new char[stemp.Length + count + _syntax.SchemeName.Length + 3 + 1];
count = 0;
//Scheme and slashes
if ((parts & UriComponents.Scheme) != 0)
{
_syntax.SchemeName.CopyTo(0, chars, count, _syntax.SchemeName.Length);
count += _syntax.SchemeName.Length;
if (parts != UriComponents.Scheme)
{
chars[count++] = ':';
if (InFact(Flags.AuthorityFound))
{
chars[count++] = '/';
chars[count++] = '/';
}
}
}
//UserInfo
if ((parts & UriComponents.UserInfo) != 0 && InFact(Flags.HasUserInfo))
{
if ((nonCanonical & (ushort)UriComponents.UserInfo) != 0)
{
switch (formatAs)
{
case UriFormat.UriEscaped:
if (NotAny(Flags.UserEscaped))
{
chars = UriHelper.EscapeString(_string, _info.Offset.User, _info.Offset.Host, chars,
ref count, true, '?', '#', '%');
}
else
{
if (InFact(Flags.E_UserNotCanonical))
{
// We should throw here but currently just accept user input known as invalid
}
_string.CopyTo(_info.Offset.User, chars, count, _info.Offset.Host - _info.Offset.User);
count += (_info.Offset.Host - _info.Offset.User);
}
break;
case UriFormat.SafeUnescaped:
chars = UriHelper.UnescapeString(_string, _info.Offset.User, _info.Offset.Host - 1,
chars, ref count, '@', '/', '\\', InFact(Flags.UserEscaped) ? UnescapeMode.Unescape :
UnescapeMode.EscapeUnescape, _syntax, false);
chars[count++] = '@';
break;
case UriFormat.Unescaped:
chars = UriHelper.UnescapeString(_string, _info.Offset.User, _info.Offset.Host, chars,
ref count, c_DummyChar, c_DummyChar, c_DummyChar,
UnescapeMode.Unescape | UnescapeMode.UnescapeAll, _syntax, false);
break;
default: //V1ToStringUnescape
chars = UriHelper.UnescapeString(_string, _info.Offset.User, _info.Offset.Host, chars,
ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly, _syntax,
false);
break;
}
}
else
{
UriHelper.UnescapeString(_string, _info.Offset.User, _info.Offset.Host, chars, ref count,
c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly, _syntax, false);
}
if (parts == UriComponents.UserInfo)
{
//strip '@' delimiter
--count;
}
}
// Host
if ((parts & UriComponents.Host) != 0 && stemp.Length != 0)
{
UnescapeMode mode;
if (formatAs != UriFormat.UriEscaped && HostType == Flags.BasicHostType
&& (nonCanonical & (ushort)UriComponents.Host) != 0)
{
// only Basic host could be in the escaped form
mode = formatAs == UriFormat.Unescaped
? (UnescapeMode.Unescape | UnescapeMode.UnescapeAll) :
(InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape);
}
else
{
mode = UnescapeMode.CopyOnly;
}
// NormalizedHost
if ((parts & UriComponents.NormalizedHost) != 0)
{
unsafe
{
fixed (char* hostPtr = stemp)
{
bool allAscii = false;
bool atLeastOneValidIdn = false;
try
{
// Upconvert any punycode to unicode, xn--pck -> ?
stemp = DomainNameHelper.UnicodeEquivalent(
hostPtr, 0, stemp.Length, ref allAscii, ref atLeastOneValidIdn);
}
// The host may be invalid punycode (www.xn--?-pck.com),
// but we shouldn't throw after the constructor.
catch (UriFormatException) { }
}
}
}
chars = UriHelper.UnescapeString(stemp, 0, stemp.Length, chars, ref count, '/', '?', '#', mode,
_syntax, false);
// A fix up only for SerializationInfo and IpV6 host with a scopeID
if ((parts & UriComponents.SerializationInfoString) != 0 && HostType == Flags.IPv6HostType &&
(object)_info.ScopeId != null)
{
_info.ScopeId.CopyTo(0, chars, count - 1, _info.ScopeId.Length);
count += _info.ScopeId.Length;
chars[count - 1] = ']';
}
}
//Port (always wants a ':' delimiter if got to this method)
if ((parts & UriComponents.Port) != 0)
{
if ((nonCanonical & (ushort)UriComponents.Port) == 0)
{
//take it from m_String
if (InFact(Flags.NotDefaultPort))
{
ushort start = _info.Offset.Path;
while (_string[--start] != ':')
{
;
}
_string.CopyTo(start, chars, count, _info.Offset.Path - start);
count += (_info.Offset.Path - start);
}
else if ((parts & UriComponents.StrongPort) != 0 && _syntax.DefaultPort != UriParser.NoDefaultPort)
{
chars[count++] = ':';
stemp = _info.Offset.PortValue.ToString(CultureInfo.InvariantCulture);
stemp.CopyTo(0, chars, count, stemp.Length);
count += stemp.Length;
}
}
else if (InFact(Flags.NotDefaultPort) || ((parts & UriComponents.StrongPort) != 0 &&
_syntax.DefaultPort != UriParser.NoDefaultPort))
{
// recreate string from port value
chars[count++] = ':';
stemp = _info.Offset.PortValue.ToString(CultureInfo.InvariantCulture);
stemp.CopyTo(0, chars, count, stemp.Length);
count += stemp.Length;
}
}
ushort delimiterAwareIndex;
//Path
if ((parts & UriComponents.Path) != 0)
{
chars = GetCanonicalPath(chars, ref count, formatAs);
// (possibly strip the leading '/' delimiter)
if (parts == UriComponents.Path)
{
if (InFact(Flags.AuthorityFound) && count != 0 && chars[0] == '/')
{
delimiterAwareIndex = 1; --count;
}
else
{
delimiterAwareIndex = 0;
}
return count == 0 ? string.Empty : new string(chars, delimiterAwareIndex, count);
}
}
//Query (possibly strip the '?' delimiter)
if ((parts & UriComponents.Query) != 0 && _info.Offset.Query < _info.Offset.Fragment)
{
delimiterAwareIndex = (ushort)(_info.Offset.Query + 1);
if (parts != UriComponents.Query)
chars[count++] = '?'; //see Fragment+1 below
if ((nonCanonical & (ushort)UriComponents.Query) != 0)
{
switch (formatAs)
{
case UriFormat.UriEscaped:
//Can Assert IsImplicitfile == false
if (NotAny(Flags.UserEscaped))
chars = UriHelper.EscapeString(_string, delimiterAwareIndex, _info.Offset.Fragment, chars,
ref count, true, '#', c_DummyChar, '%');
else
{
UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.Fragment, chars,
ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly, _syntax,
true);
}
break;
case V1ToStringUnescape:
chars = UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.Fragment, chars,
ref count, '#', c_DummyChar, c_DummyChar, (InFact(Flags.UserEscaped) ?
UnescapeMode.Unescape : UnescapeMode.EscapeUnescape) | UnescapeMode.V1ToStringFlag,
_syntax, true);
break;
case UriFormat.Unescaped:
chars = UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.Fragment, chars,
ref count, '#', c_DummyChar, c_DummyChar,
(UnescapeMode.Unescape | UnescapeMode.UnescapeAll), _syntax, true);
break;
default: // UriFormat.SafeUnescaped
chars = UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.Fragment, chars,
ref count, '#', c_DummyChar, c_DummyChar, (InFact(Flags.UserEscaped) ?
UnescapeMode.Unescape : UnescapeMode.EscapeUnescape), _syntax, true);
break;
}
}
else
{
UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.Fragment, chars, ref count,
c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly, _syntax, true);
}
}
//Fragment (possibly strip the '#' delimiter)
if ((parts & UriComponents.Fragment) != 0 && _info.Offset.Fragment < _info.Offset.End)
{
delimiterAwareIndex = (ushort)(_info.Offset.Fragment + 1);
if (parts != UriComponents.Fragment)
chars[count++] = '#'; //see Fragment+1 below
if ((nonCanonical & (ushort)UriComponents.Fragment) != 0)
{
switch (formatAs)
{
case UriFormat.UriEscaped:
if (NotAny(Flags.UserEscaped))
chars = UriHelper.EscapeString(_string, delimiterAwareIndex, _info.Offset.End, chars,
ref count, true, c_DummyChar, c_DummyChar, '%');
else
{
UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.End, chars,
ref count, c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly, _syntax,
false);
}
break;
case V1ToStringUnescape:
chars = UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.End, chars,
ref count, '#', c_DummyChar, c_DummyChar, (InFact(Flags.UserEscaped) ?
UnescapeMode.Unescape : UnescapeMode.EscapeUnescape) | UnescapeMode.V1ToStringFlag,
_syntax, false);
break;
case UriFormat.Unescaped:
chars = UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.End, chars,
ref count, '#', c_DummyChar, c_DummyChar,
UnescapeMode.Unescape | UnescapeMode.UnescapeAll, _syntax, false);
break;
default: // UriFormat.SafeUnescaped
chars = UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.End, chars,
ref count, '#', c_DummyChar, c_DummyChar, (InFact(Flags.UserEscaped) ?
UnescapeMode.Unescape : UnescapeMode.EscapeUnescape), _syntax, false);
break;
}
}
else
{
UriHelper.UnescapeString(_string, delimiterAwareIndex, _info.Offset.End, chars, ref count,
c_DummyChar, c_DummyChar, c_DummyChar, UnescapeMode.CopyOnly, _syntax, false);
}
}
return new string(chars, 0, count);
}