in Microsoft.Azure.Cosmos/src/Json/JsonTextParser.cs [225:362]
private static Utf8String UnescapeJson(Utf8Memory escapedString, bool checkIfNeedsEscaping = true)
{
if (escapedString.IsEmpty)
{
return Utf8String.Empty;
}
if (checkIfNeedsEscaping && (escapedString.Span.Span.IndexOf(JsonTextParser.ReverseSolidusBytes.Span) < 0))
{
// String doesn't need unescaping
return Utf8String.UnsafeFromUtf8BytesNoValidation(escapedString.Memory);
}
Memory<byte> stringBuffer = new byte[escapedString.Length];
escapedString.Memory.CopyTo(stringBuffer);
Span<byte> stringBufferSpan = stringBuffer.Span;
int readOffset = 0;
int writeOffset = 0;
while (readOffset != stringBuffer.Length)
{
if (stringBufferSpan[readOffset] == '\\')
{
// Consume the '\' character
readOffset++;
// Figure out how to escape.
switch (stringBufferSpan[readOffset++])
{
case (byte)'b':
stringBufferSpan[writeOffset++] = (byte)'\b';
break;
case (byte)'f':
stringBufferSpan[writeOffset++] = (byte)'\f';
break;
case (byte)'n':
stringBufferSpan[writeOffset++] = (byte)'\n';
break;
case (byte)'r':
stringBufferSpan[writeOffset++] = (byte)'\r';
break;
case (byte)'t':
stringBufferSpan[writeOffset++] = (byte)'\t';
break;
case (byte)'\\':
stringBufferSpan[writeOffset++] = (byte)'\\';
break;
case (byte)'"':
stringBufferSpan[writeOffset++] = (byte)'"';
break;
case (byte)'/':
stringBufferSpan[writeOffset++] = (byte)'/';
break;
case (byte)'u':
// parse JSON unicode code point: \uXXXX(\uYYYY)
// Start by reading XXXX, since \u is already read.
char escapeSequence = (char)0;
for (int escapeSequenceIndex = 0; escapeSequenceIndex < 4; escapeSequenceIndex++)
{
escapeSequence <<= 4;
byte currentCharacter = stringBufferSpan[readOffset++];
if (currentCharacter >= '0' && currentCharacter <= '9')
{
escapeSequence += (char)(currentCharacter - '0');
}
else if (currentCharacter >= 'A' && currentCharacter <= 'F')
{
escapeSequence += (char)(10 + currentCharacter - 'A');
}
else if (currentCharacter >= 'a' && currentCharacter <= 'f')
{
escapeSequence += (char)(10 + currentCharacter - 'a');
}
else
{
throw new JsonInvalidEscapedCharacterException();
}
}
if ((escapeSequence >= Utf16Surrogate.High.Min) && (escapeSequence <= Utf16Surrogate.High.Max))
{
// We have a high surrogate + low surrogate pair
if (stringBufferSpan[readOffset++] != '\\')
{
throw new JsonInvalidEscapedCharacterException();
}
if (stringBufferSpan[readOffset++] != 'u')
{
throw new JsonInvalidEscapedCharacterException();
}
char highSurrogate = escapeSequence;
char lowSurrogate = (char)0;
for (int escapeSequenceIndex = 0; escapeSequenceIndex < 4; escapeSequenceIndex++)
{
lowSurrogate <<= 4;
byte currentCharacter = stringBufferSpan[readOffset++];
if (currentCharacter >= '0' && currentCharacter <= '9')
{
lowSurrogate += (char)(currentCharacter - '0');
}
else if (currentCharacter >= 'A' && currentCharacter <= 'F')
{
lowSurrogate += (char)(10 + currentCharacter - 'A');
}
else if (currentCharacter >= 'a' && currentCharacter <= 'f')
{
lowSurrogate += (char)(10 + currentCharacter - 'a');
}
else
{
throw new JsonInvalidEscapedCharacterException();
}
}
writeOffset += WideCharToMultiByte(highSurrogate, lowSurrogate, stringBufferSpan.Slice(start: writeOffset));
}
else
{
writeOffset += WideCharToMultiByte(escapeSequence, stringBufferSpan.Slice(start: writeOffset));
}
break;
}
}
else
{
stringBufferSpan[writeOffset++] = stringBufferSpan[readOffset++];
}
}
return Utf8String.UnsafeFromUtf8BytesNoValidation(stringBuffer.Slice(start: 0, writeOffset));
}