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); }