private unsafe void CreateUriInfo(Flags cF)
{
UriInfo info = new UriInfo();
// This will be revisited in ParseRemaining but for now just have it at least m_String.Length
info.Offset.End = (ushort)_string.Length;
if (UserDrivenParsing)
goto Done;
ushort idx;
bool notCanonicalScheme = false;
// The m_String may have leading spaces, figure that out
// plus it will set idx value for next steps
if ((cF & Flags.ImplicitFile) != 0)
{
idx = (ushort)0;
while (UriHelper.IsLWS(_string[idx]))
{
++idx;
++info.Offset.Scheme;
}
if (StaticInFact(cF, Flags.UncPath))
{
// For implicit file AND Unc only
idx += 2;
//skip any other slashes (compatibility with V1.0 parser)
while (idx < (ushort)(cF & Flags.IndexMask) && (_string[idx] == '/' || _string[idx] == '\\'))
{
++idx;
}
}
}
else
{
// This is NOT an ImplicitFile uri
idx = (ushort)_syntax.SchemeName.Length;
while (_string[idx++] != ':')
{
++info.Offset.Scheme;
}
if ((cF & Flags.AuthorityFound) != 0)
{
if (_string[idx] == '\\' || _string[idx + 1] == '\\')
notCanonicalScheme = true;
idx += 2;
if ((cF & (Flags.UncPath | Flags.DosPath)) != 0)
{
// Skip slashes if it was allowed during ctor time
// NB: Today this is only allowed if a Unc or DosPath was found after the scheme
while (idx < (ushort)(cF & Flags.IndexMask) && (_string[idx] == '/' || _string[idx] == '\\'))
{
notCanonicalScheme = true;
++idx;
}
}
}
}
// Some schemes (mailto) do not have Authority-based syntax, still they do have a port
if (_syntax.DefaultPort != UriParser.NoDefaultPort)
info.Offset.PortValue = (ushort)_syntax.DefaultPort;
//Here we set the indexes for already parsed components
if ((cF & Flags.HostTypeMask) == Flags.UnknownHostType
|| StaticInFact(cF, Flags.DosPath)
)
{
//there is no Authority component defined
info.Offset.User = (ushort)(cF & Flags.IndexMask);
info.Offset.Host = info.Offset.User;
info.Offset.Path = info.Offset.User;
cF &= ~Flags.IndexMask;
if (notCanonicalScheme)
{
cF |= Flags.SchemeNotCanonical;
}
goto Done;
}
info.Offset.User = idx;
//Basic Host Type does not have userinfo and port
if (HostType == Flags.BasicHostType)
{
info.Offset.Host = idx;
info.Offset.Path = (ushort)(cF & Flags.IndexMask);
cF &= ~Flags.IndexMask;
goto Done;
}
if ((cF & Flags.HasUserInfo) != 0)
{
// we previously found a userinfo, get it again
while (_string[idx] != '@')
{
++idx;
}
++idx;
info.Offset.Host = idx;
}
else
{
info.Offset.Host = idx;
}
//Now reload the end of the parsed host
idx = (ushort)(cF & Flags.IndexMask);
//From now on we do not need IndexMask bits, and reuse the space for X_NotCanonical flags
//clear them now
cF &= ~Flags.IndexMask;
// If this is not canonical, don't count on user input to be good
if (notCanonicalScheme)
{
cF |= Flags.SchemeNotCanonical;
}
//Guessing this is a path start
info.Offset.Path = idx;
// parse Port if any. The new spec allows a port after ':' to be empty (assuming default?)
bool notEmpty = false;
// Note we already checked on general port syntax in ParseMinimal()
// If iri parsing is on with unicode chars then the end of parsed host
// points to m_orig string and not m_String
bool UseOrigUnicodeStrOffset = ((cF & Flags.UseOrigUncdStrOffset) != 0);
// This should happen only once. Reset it
cF &= ~Flags.UseOrigUncdStrOffset;
if (UseOrigUnicodeStrOffset)
info.Offset.End = (ushort)_originalUnicodeString.Length;
if (idx < info.Offset.End)
{
fixed (char* userString = UseOrigUnicodeStrOffset ? _originalUnicodeString : _string)
{
if (userString[idx] == ':')
{
int port = 0;
//Check on some non-canonical cases http://host:0324/, http://host:03, http://host:0, etc
if (++idx < info.Offset.End)
{
port = (ushort)(userString[idx] - '0');
if (!(port == unchecked((ushort)('/' - '0')) || port == (ushort)('?' - '0') ||
port == unchecked((ushort)('#' - '0'))))
{
notEmpty = true;
if (port == 0)
{
cF |= (Flags.PortNotCanonical | Flags.E_PortNotCanonical);
}
for (++idx; idx < info.Offset.End; ++idx)
{
ushort val = (ushort)((ushort)userString[idx] - (ushort)'0');
if (val == unchecked((ushort)('/' - '0')) || val == (ushort)('?' - '0') ||
val == unchecked((ushort)('#' - '0')))
{
break;
}
port = (port * 10 + val);
}
}
}
if (notEmpty && info.Offset.PortValue != (ushort)port)
{
info.Offset.PortValue = (ushort)port;
cF |= Flags.NotDefaultPort;
}
else
{
//This will tell that we do have a ':' but the port value does
//not follow to canonical rules
cF |= (Flags.PortNotCanonical | Flags.E_PortNotCanonical);
}
info.Offset.Path = (ushort)idx;
}
}
}
Done:
cF |= Flags.MinimalUriInfoSet;
info.DnsSafeHost = _dnsSafeHost;
lock (_string)
{
if ((_flags & Flags.MinimalUriInfoSet) == 0)
{
_info = info;
_flags = (_flags & ~Flags.IndexMask) | cF;
}
}
}