private static async ValueTask <String> ReadJSONStringAsync( CharacterReader reader, StreamReaderWithResizableBuffer stream, Boolean startQuoteRead ) { Char charRead; var proceed = startQuoteRead; if (!startQuoteRead) { stream.EraseReadBytesFromBuffer(); charRead = await reader.ReadNextCharacterAsync(stream, c => !Char.IsWhiteSpace(c)); proceed = charRead == STR_START; } String str; var encoding = reader.Encoding; var eencoding = encoding.Encoding; if (proceed) { // At this point, we have read the starting quote, now read the contents. var asciiSize = encoding.BytesPerASCIICharacter; var startIdx = stream.ReadBytesCount; async ValueTask <Int32> DecodeUnicodeEscape() { var decodeIdx = stream.ReadBytesCount; await stream.ReadMoreOrThrow(4 *asciiSize); return((encoding.ReadHexDecimal(stream.Buffer, ref decodeIdx) << 8) | (encoding.ReadHexDecimal(stream.Buffer, ref decodeIdx))); } // Read string, but mind the escapes Int32 curIdx; do { curIdx = stream.ReadBytesCount; charRead = await reader.TryReadNextCharacterAsync(stream) ?? STR_END; if (charRead == STR_ESCAPE_PREFIX) { // Escape handling - next character decides what we will do charRead = await reader.TryReadNextCharacterAsync(stream) ?? STR_END; Byte replacementByte = 0; switch (charRead) { case STR_END: case STR_ESCAPE_PREFIX: case '/': // Actual value is just just read char minus the '\' replacementByte = (Byte)charRead; break; case 'b': // Backspace replacementByte = (Byte)'\b'; break; case 'f': // Form feed replacementByte = (Byte)'\f'; break; case 'n': // New line replacementByte = (Byte)'\n'; break; case 'r': // Carriage return replacementByte = (Byte)'\r'; break; case 't': // Horizontal tab replacementByte = (Byte)'\t'; break; case 'u': // Unicode sequence - followed by four hexadecimal digits var code = await DecodeUnicodeEscape(); if (code <= Char.MaxValue && code >= Char.MinValue && Char.IsSurrogate((charRead = (Char)code))) { var idxAfterDecode = stream.ReadBytesCount; Char?nullableChar; if ( (nullableChar = await reader.TryReadNextCharacterAsync(stream)).HasValue && nullableChar.Value == STR_ESCAPE_PREFIX && (nullableChar = await reader.TryReadNextCharacterAsync(stream)).HasValue && nullableChar.Value == 'u' ) { var code2 = await DecodeUnicodeEscape(); reader.GetBytes(charRead, (Char)code2, stream.Buffer, ref curIdx); } else { // Orphaned surrogate character... stream.UnreadBytes(stream.ReadBytesCount - idxAfterDecode); reader.GetBytes(charRead, stream.Buffer, ref curIdx); } } else { var codeStr = Char.ConvertFromUtf32(code); // Overwrite '\uXXXX' with actual character curIdx += eencoding.GetBytes(codeStr, 0, codeStr.Length, stream.Buffer, curIdx); } break; default: // Just let it slide curIdx = stream.ReadBytesCount; break; } if (replacementByte > 0) { // We just read ASCII char, which should be now replaced encoding.WriteASCIIByte(stream.Buffer, ref curIdx, replacementByte); } // Erase anything extra stream.EraseReadBufferSegment(curIdx, stream.ReadBytesCount - curIdx); // Always read next char charRead = (Char)0; } } while (charRead != STR_END); var strByteCount = stream.ReadBytesCount - startIdx - asciiSize; str = strByteCount <= 0 ? "" : eencoding.GetString(stream.Buffer, startIdx, strByteCount); } else { str = null; } return(str); }