/// <summary> /// Read more items into the buffer of this <see cref="MemorizingPotentiallyAsyncReader{TValue, TBufferItem}"/>, or throws if not enough items could be read. /// </summary> /// <param name="reader">This <see cref="MemorizingPotentiallyAsyncReader{TValue, TBufferItem}"/>.</param> /// <param name="amountToRead">The amount of items to read.</param> /// <returns>A task always returning <c>true</c>.</returns> /// <exception cref="NullReferenceException">If this <see cref="MemorizingPotentiallyAsyncReader{TValue, TBufferItem}"/> is <c>null</c>.</exception> /// <exception cref="EndOfStreamException">If not enough items could be read.</exception> public static async ValueTask <Boolean> ReadMoreOrThrow <TValue>( this MemorizingPotentiallyAsyncReader <TValue?, TValue> reader, Int32 amountToRead ) where TValue : struct { if (await reader.TryReadMore(amountToRead) != amountToRead) { throw new EndOfStreamException(); } return(true); }
private static async ValueTask <String> ReadJSONStringAsync( MemorizingPotentiallyAsyncReader <Char?, Char> reader, Boolean startQuoteRead ) { Char charRead; var proceed = startQuoteRead; if (!proceed) { charRead = await reader.ReadUntilAsync(c => !Char.IsWhiteSpace(c)); proceed = charRead == Consts.STR_START; } String str; if (proceed) { reader.ClearBuffer(); // At this point, we have read the starting quote, now read the contents. async ValueTask <Char> DecodeUnicodeEscape() { Char decoded; var decodeStartIdx = reader.BufferCount; if (await reader.TryReadMore(4) == 4) { var array = reader.Buffer; decoded = (Char)((array[decodeStartIdx].GetHexadecimalValue().GetValueOrDefault() << 12) | (array[decodeStartIdx + 1].GetHexadecimalValue().GetValueOrDefault() << 8) | (array[decodeStartIdx + 2].GetHexadecimalValue().GetValueOrDefault() << 4) | array[decodeStartIdx + 3].GetHexadecimalValue().GetValueOrDefault()); } else { decoded = '\0'; } return(decoded); } // Read string, but mind the escapes // TODO maybe do reader.PeekUntilAsync( c => c == STR_END && reader.Buffer[reader.BufferCount - 2] != STR_ESCAPE ); // And then do escaping in-place...? Int32 curIdx; do { curIdx = reader.BufferCount; charRead = (await reader.TryReadNextAsync()) ?? Consts.STR_END; if (charRead == Consts.STR_ESCAPE_PREFIX) { // Escape handling - next character decides what we will do charRead = (await reader.TryReadNextAsync()) ?? Consts.STR_END; Char replacementByte = '\0'; switch (charRead) { case Consts.STR_END: case Consts.STR_ESCAPE_PREFIX: case '/': // Actual value is just just read char minus the '\' replacementByte = charRead; break; case 'b': // Backspace replacementByte = '\b'; break; case 'f': // Form feed replacementByte = '\f'; break; case 'n': // New line replacementByte = '\n'; break; case 'r': // Carriage return replacementByte = '\r'; break; case 't': // Horizontal tab replacementByte = '\t'; break; case 'u': // Unicode sequence - followed by four hexadecimal digits var decoded = await DecodeUnicodeEscape(); reader.Buffer[curIdx++] = decoded; break; default: // Just let it slide curIdx = reader.BufferCount; break; } if (replacementByte > 0) { // We just read ASCII char, which should be now replaced reader.Buffer[curIdx++] = replacementByte; } // Erase anything extra reader.EraseBufferSegment(curIdx, reader.BufferCount - curIdx); // Always read next char charRead = (Char)0; } } while (charRead != Consts.STR_END); var strCharCount = reader.BufferCount - 1; if (strCharCount <= 0) { str = ""; } else { str = new String(reader.Buffer, 0, strCharCount); } } else { str = null; } return(str); }