private static object?ReadValueCore(ref Utf8JsonReader reader, Type returnType, JsonSerializerOptions?options) { if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } ReadStack state = default; state.InitializeRoot(returnType, options); ReadValueCore(options, ref reader, ref state); return(state.Current.ReturnValue); }
private static async ValueTask <TValue> ReadAsync <TValue>( Stream utf8Json, Type returnType, JsonSerializerOptions?options = null, CancellationToken cancellationToken = default) { if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } ReadStack state = default; state.InitializeRoot(returnType, options); // Ensures converters support contination due to having to re-populate the buffer from a Stream. state.SupportContinuation = true; var readerState = new JsonReaderState(options.GetReaderOptions()); // todo: switch to ArrayBuffer implementation to handle and simplify the allocs? int utf8BomLength = JsonConstants.Utf8Bom.Length; byte[] buffer = ArrayPool <byte> .Shared.Rent(Math.Max(options.DefaultBufferSize, utf8BomLength)); int bytesInBuffer = 0; long totalBytesRead = 0; int clearMax = 0; bool isFirstIteration = true; 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; } int start = 0; if (isFirstIteration) { isFirstIteration = false; // Handle the UTF-8 BOM if present Debug.Assert(buffer.Length >= JsonConstants.Utf8Bom.Length); if (buffer.AsSpan().StartsWith(JsonConstants.Utf8Bom)) { start += utf8BomLength; bytesInBuffer -= utf8BomLength; } } // Process the data available ReadCore( ref readerState, isFinalBlock, new ReadOnlySpan <byte>(buffer, start, bytesInBuffer), options, ref state); Debug.Assert(state.BytesConsumed <= bytesInBuffer); int bytesConsumed = checked ((int)state.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 + start, 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 + start, 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); } // The reader should have thrown if we have remaining bytes. Debug.Assert(bytesInBuffer == 0); return((TValue)state.Current.ReturnValue !); }