internal unsafe static char[] UnescapeString(char *pStr, int start, int end, char[] dest, ref int destPosition,
char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser syntax, bool isQuery)
{
byte [] bytes = null;
byte escapedReallocations = 0;
bool escapeReserved = false;
int next = start;
bool iriParsing = Uri.IriParsingStatic(syntax) &&
((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.EscapeUnescape);
while (true)
{
// we may need to re-pin dest[]
fixed(char *pDest = dest)
{
if ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.CopyOnly)
{
while (start < end)
{
pDest[destPosition++] = pStr[start++];
}
return(dest);
}
while (true)
{
char ch = (char)0;
for (; next < end; ++next)
{
if ((ch = pStr[next]) == '%')
{
if ((unescapeMode & UnescapeMode.Unescape) == 0)
{
// re-escape, don't check anything else
escapeReserved = true;
}
else if (next + 2 < end)
{
ch = EscapedAscii(pStr[next + 1], pStr[next + 2]);
// Unescape a good sequence if full unescape is requested
if (unescapeMode >= UnescapeMode.UnescapeAll)
{
if (ch == Uri.c_DummyChar)
{
if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow)
{
// Should be a rare case where the app tries to feed an invalid escaped sequence
throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
}
continue;
}
}
// re-escape % from an invalid sequence
else if (ch == Uri.c_DummyChar)
{
if ((unescapeMode & UnescapeMode.Escape) != 0)
{
escapeReserved = true;
}
else
{
continue; // we should throw instead but since v1.0 woudl just print '%'
}
}
// Do not unescape '%' itself unless full unescape is requested
else if (ch == '%')
{
next += 2;
continue;
}
// Do not unescape a reserved char unless full unescape is requested
else if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3)
{
next += 2;
continue;
}
// Do not unescape a dangerous char unless it's V1ToStringFlags mode
else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 && IsNotSafeForUnescape(ch))
{
next += 2;
continue;
}
else if (iriParsing && ((ch <= '\x9F' && IsNotSafeForUnescape(ch)) ||
(ch > '\x9F' && !IriHelper.CheckIriUnicodeRange(ch, isQuery))))
{
// check if unenscaping gives a char ouside iri range
// if it does then keep it escaped
next += 2;
continue;
}
// unescape escaped char or escape %
break;
}
else if (unescapeMode >= UnescapeMode.UnescapeAll)
{
if (unescapeMode >= UnescapeMode.UnescapeAllOrThrow)
{
// Should be a rare case where the app tries to feed an invalid escaped sequence
throw new UriFormatException(SR.GetString(SR.net_uri_BadString));
}
// keep a '%' as part of a bogus sequence
continue;
}
else
{
escapeReserved = true;
}
// escape (escapeReserved==ture) or otheriwse unescape the sequence
break;
}
else if ((unescapeMode & (UnescapeMode.Unescape | UnescapeMode.UnescapeAll))
== (UnescapeMode.Unescape | UnescapeMode.UnescapeAll))
{
continue;
}
else if ((unescapeMode & UnescapeMode.Escape) != 0)
{
// Could actually escape some of the characters
if (ch == rsvd1 || ch == rsvd2 || ch == rsvd3)
{
// found an unescaped reserved character -> escape it
escapeReserved = true;
break;
}
else if ((unescapeMode & UnescapeMode.V1ToStringFlag) == 0 &&
(ch <= '\x1F' || (ch >= '\x7F' && ch <= '\x9F')))
{
// found an unescaped reserved character -> escape it
escapeReserved = true;
break;
}
}
}
//copy off previous characters from input
while (start < next)
{
pDest[destPosition++] = pStr[start++];
}
if (next != end)
{
//VsWhidbey#87423
if (escapeReserved)
{
//escape that char
// Since this should be _really_ rare case, reallocate with constant size increase of 30 rsvd-type characters.
if (escapedReallocations == 0)
{
escapedReallocations = 30;
char[] newDest = new char[dest.Length + escapedReallocations * 3];
fixed(char *pNewDest = newDest)
{
for (int i = 0; i < destPosition; ++i)
{
pNewDest[i] = pDest[i];
}
}
dest = newDest;
// re-pin new dest[] array
goto dest_fixed_loop_break;
}
else
{
--escapedReallocations;
EscapeAsciiChar(pStr[next], dest, ref destPosition);
escapeReserved = false;
start = ++next;
continue;
}
}
// unescaping either one Ascii or possibly multiple Unicode
if (ch <= '\x7F')
{
//ASCII
dest[destPosition++] = ch;
next += 3;
start = next;
continue;
}
// Unicode
int byteCount = 1;
// lazy initialization of max size, will reuse the array for next sequences
if ((object)bytes == null)
{
bytes = new byte[end - next];
}
bytes[0] = (byte)ch;
next += 3;
while (next < end)
{
// Check on exit criterion
if ((ch = pStr[next]) != '%' || next + 2 >= end)
{
break;
}
// already made sure we have 3 characters in str
ch = EscapedAscii(pStr[next + 1], pStr[next + 2]);
//invalid hex sequence ?
if (ch == Uri.c_DummyChar)
{
break;
}
// character is not part of a UTF-8 sequence ?
else if (ch < '\x80')
{
break;
}
else
{
//a UTF-8 sequence
bytes[byteCount++] = (byte)ch;
next += 3;
}
}
Encoding noFallbackCharUTF8 = (Encoding)Encoding.UTF8.Clone();
noFallbackCharUTF8.EncoderFallback = new EncoderReplacementFallback("");
noFallbackCharUTF8.DecoderFallback = new DecoderReplacementFallback("");
char[] unescapedChars = new char[bytes.Length];
int charCount = noFallbackCharUTF8.GetChars(bytes, 0, byteCount, unescapedChars, 0);
start = next;
// match exact bytes
// Do not unescape chars not allowed by Iri
// need to check for invalid utf sequences that may not have given any chars
MatchUTF8Sequence(pDest, dest, ref destPosition, unescapedChars, charCount, bytes,
byteCount, isQuery, iriParsing);
}
if (next == end)
{
goto done;
}
}
dest_fixed_loop_break :;
}
}
done : return(dest);
}