コード例 #1
0
        private static async ValueTask <TValue> ReadAsync <TValue>(
            Stream utf8Json,
            Type returnType,
            JsonSerializerOptions options       = null,
            CancellationToken cancellationToken = default)
        {
            options ??= JsonSerializerOptions.s_defaultOptions;

            ReadStack state = default;

            state.Current.Initialize(returnType, options);

            var readerState = new JsonReaderState(options.GetReaderOptions());

            // todo: switch to ArrayBuffer implementation to handle and simplify the allocs?
            byte[] buffer = ArrayPool <byte> .Shared.Rent(options.DefaultBufferSize);

            int  bytesInBuffer  = 0;
            long totalBytesRead = 0;
            int  clearMax       = 0;

            try
            {
                while (true)
                {
                    // Read from the stream until either our buffer is filled or we hit EOF.
                    // Calling ReadCore is relatively expensive, so we minimize the number of times
                    // we need to call it.
                    bool isFinalBlock = false;
                    while (true)
                    {
                        int bytesRead = await utf8Json.ReadAsync(
#if BUILDING_INBOX_LIBRARY
                            buffer.AsMemory(bytesInBuffer),
#else
                            buffer, bytesInBuffer, buffer.Length - bytesInBuffer,
#endif
                            cancellationToken).ConfigureAwait(false);

                        if (bytesRead == 0)
                        {
                            isFinalBlock = true;
                            break;
                        }

                        totalBytesRead += bytesRead;
                        bytesInBuffer  += bytesRead;

                        if (bytesInBuffer == buffer.Length)
                        {
                            break;
                        }
                    }

                    if (bytesInBuffer > clearMax)
                    {
                        clearMax = bytesInBuffer;
                    }

                    // Process the data available
                    ReadCore(
                        ref readerState,
                        isFinalBlock,
                        new Span <byte>(buffer, 0, bytesInBuffer),
                        options,
                        ref state);

                    Debug.Assert(readerState.BytesConsumed <= bytesInBuffer);
                    int bytesConsumed = (int)readerState.BytesConsumed;
                    bytesInBuffer -= bytesConsumed;

                    if (isFinalBlock)
                    {
                        break;
                    }

                    // Check if we need to shift or expand the buffer because there wasn't enough data to complete deserialization.
                    if ((uint)bytesInBuffer > ((uint)buffer.Length / 2))
                    {
                        // We have less than half the buffer available, double the buffer size.
                        byte[] dest = ArrayPool <byte> .Shared.Rent((buffer.Length < (int.MaxValue / 2))?buffer.Length * 2 : int.MaxValue);

                        // Copy the unprocessed data to the new buffer while shifting the processed bytes.
                        Buffer.BlockCopy(buffer, bytesConsumed, dest, 0, bytesInBuffer);

                        new Span <byte>(buffer, 0, clearMax).Clear();
                        ArrayPool <byte> .Shared.Return(buffer);

                        clearMax = bytesInBuffer;
                        buffer   = dest;
                    }
                    else if (bytesInBuffer != 0)
                    {
                        // Shift the processed bytes to the beginning of buffer to make more room.
                        Buffer.BlockCopy(buffer, bytesConsumed, buffer, 0, bytesInBuffer);
                    }
                }
            }
            finally
            {
                // Clear only what we used and return the buffer to the pool
                new Span <byte>(buffer, 0, clearMax).Clear();
                ArrayPool <byte> .Shared.Return(buffer);
            }

            if (bytesInBuffer != 0)
            {
                throw new JsonReaderException(
                          SR.Format(SR.DeserializeDataRemaining, totalBytesRead, bytesInBuffer),
                          readerState);
            }

            return((TValue)state.Current.ReturnValue);
        }