/// <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);
    }
Exemple #2
0
        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);
        }