Esempio n. 1
0
        /// <inheritdoc />
        public async ValueTask <Int32> TryWriteAsync(IEnumerable <Char> value, StreamWriterWithResizableBuffer sink)
        {
            if (Interlocked.CompareExchange(ref this._state, BUSY, IDLE) == IDLE)
            {
                try
                {
                    var total    = 0;
                    var auxArray = this._auxArray;
                    var encoding = this._encoding.Encoding;
                    using (var enumerator = value.GetEnumerator())
                    {
                        while (enumerator.MoveNext())
                        {
                            var c = enumerator.Current;
                            auxArray[0] = c;
                            var charCount = 1;
                            if (Char.IsHighSurrogate(c) && enumerator.MoveNext())
                            {
                                // Must read next char
                                auxArray[1] = enumerator.Current;
                                ++charCount;
                            }
                            var count = this._maxSingleCharSize * charCount;
                            if (sink.ReservedBufferCount + count > this._maxBufferSize)
                            {
                                await sink.FlushAsync();
                            }
                            Int32 offset;
                            (offset, count) = sink.ReserveBufferSegment(count);
                            if (count > 0)
                            {
                                var actualCount = encoding.GetBytes(auxArray, 0, charCount, sink.Buffer, offset);
                                total += actualCount;
                                sink.UnreserveBufferSegment(count - actualCount);
                            }
                        }
                    }

                    if (total > 0)
                    {
                        await sink.FlushAsync();
                    }

                    return(total);
                }
                finally
                {
                    Interlocked.Exchange(ref this._state, IDLE);
                }
            }
            else
            {
                throw BusyException();
            }
        }
Esempio n. 2
0
 /// <summary>
 /// Asynchronously writes JSON value (array, object, or primitive value) to this <see cref="StreamWriterWithResizableBuffer"/> using given <see cref="IEncodingInfo"/>.
 /// Tries to keep the buffer of this stream as little as possible, and allocating as little as possible any other extra objects than created JSON objects (currently parsing a <see cref="Double"/> needs to allocate string).
 /// </summary>
 /// <param name="stream">This <see cref="StreamWriterWithResizableBuffer"/>.</param>
 /// <param name="encoding">The <see cref="IEncodingInfo"/> to use.</param>
 /// <param name="jsonValue">The JSON value.</param>
 /// <returns>A task which when completed will contain the amount of bytes written to this stream.</returns>
 /// <exception cref="NullReferenceException">If this <see cref="StreamWriterWithResizableBuffer"/> is <c>null</c>.</exception>
 /// <exception cref="ArgumentNullException">If <paramref name="encoding"/> is <c>null</c>; or if <paramref name="jsonValue"/> or any of the JSON values it contains is <c>null</c>.</exception>
 /// <exception cref="NotSupportedException">When a <see cref="JToken"/> is encountered which is not <see cref="JArray"/>, <see cref="JObject"/> or <see cref="JValue"/>; or when the value of <see cref="JValue"/> is not recognized.</exception>
 public static ValueTask <Int32> WriteJSONTTokenAsync(
     this StreamWriterWithResizableBuffer stream,
     IEncodingInfo encoding,
     JToken jsonValue
     )
 {
     return(PerformWriteJSONTTokenAsync(
                ArgumentValidator.ValidateNotNullReference(stream),
                ArgumentValidator.ValidateNotNull(nameof(encoding), encoding),
                jsonValue
                ));
 }
Esempio n. 3
0
    private static Int32 WriteJSONString(
        StreamWriterWithResizableBuffer stream,
        IEncodingInfo encoding,
        String str
        )
    {
        // Write starting quote
        var asciiSize = encoding.BytesPerASCIICharacter;
        var eencoding = encoding.Encoding;
        // Allocate enough bytes for whole string and 2 quotes, we will allocate more as we encounter escapable characters
        var range = stream.ReserveBufferSegment(eencoding.GetByteCount(str) + 2 * asciiSize);
        var array = stream.Buffer;
        var start = range.Offset;
        var idx   = start;

        encoding.WriteASCIIByte(array, ref idx, (Byte)STR_START);

        // Write contents in chunks
        var prevStrIdx = 0;

        for (var i = 0; i < str.Length; ++i)
        {
            var c = str[i];
            if (NeedsEscaping(c))
            {
                // Append previous chunk
                idx += eencoding.GetBytes(str, prevStrIdx, i - prevStrIdx, array, idx);
                // Make sure we have room for escape character
                stream.ReserveBufferSegment(asciiSize);
                array = stream.Buffer;
                // Append escape character
                encoding.WriteASCIIByte(array, ref idx, (Byte)STR_ESCAPE_PREFIX);
                // Append escape sequence latter character
                TransformToEscape(encoding, array, ref idx, c);
                // Update index
                prevStrIdx = i + 1;
            }
        }

        // Append final chunk
        var finalChunkSize = str.Length - prevStrIdx;

        if (finalChunkSize > 0)
        {
            idx += eencoding.GetBytes(str, prevStrIdx, finalChunkSize, array, idx);
        }

        // Append closing quote
        encoding.WriteASCIIByte(array, ref idx, (Byte)STR_END);

        return(idx - start);
    }
Esempio n. 4
0
    private static async ValueTask <Int32> PerformWriteJSONTTokenAsync(
        StreamWriterWithResizableBuffer stream,
        IEncodingInfo encoding,
        JToken jsonValue
        )
    {
        ArgumentValidator.ValidateNotNull(nameof(jsonValue), jsonValue);
        Int32 bytesWritten;
        var   asciiSize = encoding.BytesPerASCIICharacter;
        Int32 max;

        (Int32 Offset, Int32 Count)range;
        switch (jsonValue)
        {
        case JArray array:
            range = stream.ReserveBufferSegment(asciiSize);
            encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)ARRAY_START);
            bytesWritten = range.Count;
            max          = array.Count;
            if (max > 0)
            {
                for (var i = 0; i < max; ++i)
                {
                    bytesWritten += await PerformWriteJSONTTokenAsync(stream, encoding, array[i]);

                    if (i < max - 1)
                    {
                        range = stream.ReserveBufferSegment(asciiSize);
                        encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)ARRAY_VALUE_DELIM);
                        bytesWritten += range.Count;
                    }
                }
            }
            range = stream.ReserveBufferSegment(asciiSize);
            encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)ARRAY_END);
            bytesWritten += await stream.FlushAsync();

            break;

        case JObject obj:
            range = stream.ReserveBufferSegment(asciiSize);
            encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)OBJ_START);
            bytesWritten = range.Count;
            max          = obj.Count;
            if (max > 0)
            {
                var j = 0;
                foreach (var kvp in obj)
                {
                    bytesWritten += WriteJSONString(stream, encoding, kvp.Key);
                    range         = stream.ReserveBufferSegment(asciiSize);
                    encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)OBJ_KEY_VALUE_DELIM);
                    bytesWritten += range.Count;
                    await stream.FlushAsync();

                    bytesWritten += await PerformWriteJSONTTokenAsync(stream, encoding, kvp.Value);

                    if (++j < max)
                    {
                        range = stream.ReserveBufferSegment(asciiSize);
                        encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)OBJ_VALUE_DELIM);
                        bytesWritten += range.Count;
                    }
                }
            }
            range = stream.ReserveBufferSegment(asciiSize);
            encoding.WriteASCIIByte(stream.Buffer, ref range.Offset, (Byte)OBJ_END);
            bytesWritten += await stream.FlushAsync();

            break;

        case JValue value:
            var val = value.Value;
            switch (val)
            {
            case String str:
                bytesWritten = WriteJSONString(stream, encoding, str);
                break;

            case Boolean boolean:
                // Write 'true' or 'false'
                range = stream.ReserveBufferSegment(boolean ? 4 : 5);
                encoding.WriteString(stream.Buffer, ref range.Offset, boolean ? "true" : "false");
                bytesWritten = range.Count;
                break;

            case Int64 i64:
                range = stream.ReserveBufferSegment(encoding.GetTextualIntegerRepresentationSize(i64));
                encoding.WriteIntegerTextual(stream.Buffer, ref range.Offset, i64);
                bytesWritten = range.Count;
                break;

            case Double dbl:
                // Have to allocate string :/
                var dblStr = dbl.ToString(System.Globalization.CultureInfo.InvariantCulture);
                range = stream.ReserveBufferSegment(encoding.Encoding.GetByteCount(dblStr));
                encoding.WriteString(stream.Buffer, ref range.Offset, dblStr);
                bytesWritten = range.Count;
                break;

            case null:
                // Write 'null'
                range = stream.ReserveBufferSegment(asciiSize * 4);
                encoding.WriteString(stream.Buffer, ref range.Offset, "null");
                bytesWritten = range.Count;
                break;

            default:
                throw new NotSupportedException($"Unsupported primitive value {val}.");
            }
            // Remember to flush stream
            await stream.FlushAsync();

            break;

        default:
            throw new NotSupportedException($"Unrecognized JToken type: {jsonValue?.GetType()}.");
        }

        return(bytesWritten);
    }