private static TValue?ReadFromSpan <TValue>(ReadOnlySpan <byte> utf8Json, JsonTypeInfo jsonTypeInfo, int?actualByteCount = null) { JsonSerializerOptions options = jsonTypeInfo.Options; var readerState = new JsonReaderState(options.GetReaderOptions()); var reader = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState); ReadStack state = default; state.Initialize(jsonTypeInfo); TValue? value; JsonConverter jsonConverter = jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase; // For performance, the code below is a lifted ReadCore() above. if (jsonConverter is JsonConverter <TValue> converter) { // Call the strongly-typed ReadCore that will not box structs. value = converter.ReadCore(ref reader, options, ref state); } else { // The non-generic API was called or we have a polymorphic case where TValue is not equal to the T in JsonConverter<T>. object?objValue = jsonConverter.ReadCoreAsObject(ref reader, options, ref state); Debug.Assert(objValue == null || objValue is TValue); value = (TValue?)objValue; } // The reader should have thrown if we have remaining bytes. Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length)); return(value); }
static async IAsyncEnumerable <TValue> CreateAsyncEnumerableDeserializer( Stream utf8Json, JsonSerializerOptions options, [EnumeratorCancellation] CancellationToken cancellationToken) { var bufferState = new ReadAsyncBufferState(options.DefaultBufferSize); ReadStack readStack = default; readStack.Initialize(typeof(Queue <TValue>), options, supportContinuation: true); JsonConverter converter = readStack.Current.JsonPropertyInfo !.ConverterBase; var jsonReaderState = new JsonReaderState(options.GetReaderOptions()); try { do { bufferState = await ReadFromStreamAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false); ContinueDeserialize <Queue <TValue> >(ref bufferState, ref jsonReaderState, ref readStack, converter, options); if (readStack.Current.ReturnValue is Queue <TValue> queue) { while (queue.Count > 0) { yield return(queue.Dequeue()); } } }while (!bufferState.IsFinalBlock); } finally { bufferState.Dispose(); } }
internal static TValue?ReadAll <TValue>( Stream utf8Json, JsonTypeInfo jsonTypeInfo) { JsonSerializerOptions options = jsonTypeInfo.Options; var bufferState = new ReadBufferState(options.DefaultBufferSize); ReadStack readStack = default; jsonTypeInfo.EnsureConfigured(); readStack.Initialize(jsonTypeInfo, supportContinuation: true); JsonConverter converter = readStack.Current.JsonPropertyInfo !.ConverterBase; var jsonReaderState = new JsonReaderState(options.GetReaderOptions()); try { while (true) { bufferState = ReadFromStream(utf8Json, bufferState); TValue value = ContinueDeserialize <TValue>(ref bufferState, ref jsonReaderState, ref readStack, converter, options); if (bufferState.IsFinalBlock) { return(value !); } } } finally { bufferState.Dispose(); } }
static async IAsyncEnumerable <TValue> CreateAsyncEnumerableDeserializer( Stream utf8Json, JsonSerializerOptions options, [EnumeratorCancellation] CancellationToken cancellationToken) { var bufferState = new ReadBufferState(options.DefaultBufferSize); // Hardcode the queue converter to avoid accidental use of custom converters JsonConverter converter = QueueOfTConverter <Queue <TValue>, TValue> .Instance; JsonTypeInfo jsonTypeInfo = CreateQueueJsonTypeInfo <TValue>(converter, options); ReadStack readStack = default; readStack.Initialize(jsonTypeInfo, supportContinuation: true); var jsonReaderState = new JsonReaderState(options.GetReaderOptions()); try { do { bufferState = await ReadFromStreamAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false); ContinueDeserialize <Queue <TValue> >(ref bufferState, ref jsonReaderState, ref readStack, converter, options); if (readStack.Current.ReturnValue is Queue <TValue> queue) { while (queue.Count > 0) { yield return(queue.Dequeue()); } } }while (!bufferState.IsFinalBlock); } finally { bufferState.Dispose(); } }
private static TValue?Read <TValue>(ref Utf8JsonReader reader, JsonTypeInfo jsonTypeInfo) { Debug.Assert(jsonTypeInfo.IsConfigured); if (reader.CurrentState.Options.CommentHandling == JsonCommentHandling.Allow) { ThrowHelper.ThrowArgumentException_SerializerDoesNotSupportComments(nameof(reader)); } ReadStack state = default; state.Initialize(jsonTypeInfo); Utf8JsonReader restore = reader; try { Utf8JsonReader scopedReader = GetReaderScopedToNextValue(ref reader, ref state); return(ReadCore <TValue>(ref scopedReader, jsonTypeInfo, ref state)); } catch (JsonException) { reader = restore; throw; } }
private static TValue?ReadFromSpan <TValue>(ReadOnlySpan <byte> utf8Json, JsonTypeInfo jsonTypeInfo, int?actualByteCount = null) { Debug.Assert(jsonTypeInfo.IsConfigured); JsonSerializerOptions options = jsonTypeInfo.Options; var readerState = new JsonReaderState(options.GetReaderOptions()); var reader = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState); ReadStack state = default; state.Initialize(jsonTypeInfo); TValue?value; // For performance, the code below is a lifted ReadCore() above. if (jsonTypeInfo is JsonTypeInfo <TValue> typedInfo) { // Call the strongly-typed ReadCore that will not box structs. value = typedInfo.EffectiveConverter.ReadCore(ref reader, options, ref state); } else { // The non-generic API was called. object?objValue = jsonTypeInfo.Converter.ReadCoreAsObject(ref reader, options, ref state); Debug.Assert(objValue is null or TValue); value = (TValue?)objValue; } // The reader should have thrown if we have remaining bytes. Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length)); return(value); }
private static TValue ReadCore <TValue>(ref Utf8JsonReader reader, Type returnType, JsonSerializerOptions options) { ReadStack state = default; state.Initialize(returnType, options, supportContinuation: false); JsonConverter jsonConverter = state.Current.JsonPropertyInfo !.ConverterBase; return(ReadCore <TValue>(jsonConverter, ref reader, options, ref state)); }
/// <summary> /// Reads one JSON value (including objects or arrays) from the provided reader into a <typeparamref name="TValue"/>. /// </summary> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="reader">The reader to read.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// <typeparamref name="TValue"/> is not compatible with the JSON, /// or a value could not be read from the reader. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="reader"/> is using unsupported options. /// </exception> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> /// <remarks> /// <para> /// If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/> /// is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the /// reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine /// the start of the value. /// </para> /// /// <para> /// Upon completion of this method <paramref name="reader"/> will be positioned at the /// final token in the JSON value. If an exception is thrown the reader is reset to /// the state it was in when the method was called. /// </para> /// /// <para> /// This method makes a copy of the data the reader acted on, so there is no caller /// requirement to maintain data integrity beyond the return of this method. /// </para> /// /// <para> /// The <see cref="JsonReaderOptions"/> used to create the instance of the <see cref="Utf8JsonReader"/> take precedence over the <see cref="JsonSerializerOptions"/> when they conflict. /// Hence, <see cref="JsonReaderOptions.AllowTrailingCommas"/>, <see cref="JsonReaderOptions.MaxDepth"/>, <see cref="JsonReaderOptions.CommentHandling"/> are used while reading. /// </para> /// </remarks> public static TValue?Deserialize <TValue>(ref Utf8JsonReader reader, JsonTypeInfo <TValue> jsonTypeInfo) { if (jsonTypeInfo == null) { throw new ArgumentNullException(nameof(jsonTypeInfo)); } ReadStack state = default; state.Initialize(jsonTypeInfo); return(ReadValueCore <TValue>(jsonTypeInfo.Options, ref reader, ref state)); }
/// <summary> /// Reads one JSON value (including objects or arrays) from the provided reader into a <typeparamref name="TValue"/>. /// </summary> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="reader">The reader to read.</param> /// <param name="options">Options to control the serializer behavior during reading.</param> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// <typeparamref name="TValue"/> is not compatible with the JSON, /// or a value could not be read from the reader. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="reader"/> is using unsupported options. /// </exception> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> /// <remarks> /// <para> /// If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/> /// is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the /// reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine /// the start of the value. /// </para> /// /// <para> /// Upon completion of this method <paramref name="reader"/> will be positioned at the /// final token in the JSON value. If an exception is thrown the reader is reset to /// the state it was in when the method was called. /// </para> /// /// <para> /// This method makes a copy of the data the reader acted on, so there is no caller /// requirement to maintain data integrity beyond the return of this method. /// </para> /// /// <para> /// The <see cref="JsonReaderOptions"/> used to create the instance of the <see cref="Utf8JsonReader"/> take precedence over the <see cref="JsonSerializerOptions"/> when they conflict. /// Hence, <see cref="JsonReaderOptions.AllowTrailingCommas"/>, <see cref="JsonReaderOptions.MaxDepth"/>, <see cref="JsonReaderOptions.CommentHandling"/> are used while reading. /// </para> /// </remarks> public static TValue?Deserialize <[DynamicallyAccessedMembers(MembersAccessedOnRead)] TValue>(ref Utf8JsonReader reader, JsonSerializerOptions?options = null) { if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } ReadStack state = default; state.Initialize(typeof(TValue), options, supportContinuation: false); return(ReadValueCore <TValue>(options, ref reader, ref state)); }
private static async IAsyncEnumerable <TValue> CreateAsyncEnumerableDeserializer <TValue>( Stream utf8Json, JsonTypeInfo jsonTypeInfo, [EnumeratorCancellation] CancellationToken cancellationToken) { JsonSerializerOptions options = jsonTypeInfo.Options; JsonTypeInfo <Queue <TValue> > queueTypeInfo = JsonMetadataServices.CreateQueueInfo <Queue <TValue>, TValue>( options: options, collectionInfo: new() { ObjectCreator = () => new Queue <TValue>(), ElementInfo = jsonTypeInfo, NumberHandling = options.NumberHandling }); var bufferState = new ReadBufferState(options.DefaultBufferSize); ReadStack readStack = default; queueTypeInfo.EnsureConfigured(); readStack.Initialize(queueTypeInfo, supportContinuation: true); var jsonReaderState = new JsonReaderState(options.GetReaderOptions()); try { do { bufferState = await ReadFromStreamAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false); ContinueDeserialize <Queue <TValue> >( ref bufferState, ref jsonReaderState, ref readStack, queueTypeInfo.PropertyInfoForTypeInfo.ConverterBase, options); if (readStack.Current.ReturnValue is Queue <TValue> queue) { while (queue.Count > 0) { yield return(queue.Dequeue()); } } }while (!bufferState.IsFinalBlock); } finally { bufferState.Dispose(); } }
private static TValue?ReadUsingMetadata <TValue>(ReadOnlySpan <byte> utf8Json, JsonTypeInfo jsonTypeInfo, int?actualByteCount = null) { JsonSerializerOptions options = jsonTypeInfo.Options; var readerState = new JsonReaderState(options.GetReaderOptions()); var reader = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState); ReadStack state = default; state.Initialize(jsonTypeInfo); TValue?value = ReadCore <TValue>(jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase, ref reader, options, ref state); // The reader should have thrown if we have remaining bytes. Debug.Assert(reader.BytesConsumed == (actualByteCount ?? utf8Json.Length)); return(value); }
private static TValue?DeserializeUsingMetadata <TValue>(ReadOnlySpan <char> json, JsonTypeInfo?jsonTypeInfo) { if (jsonTypeInfo == null) { throw new ArgumentNullException(nameof(jsonTypeInfo)); } ReadStack state = default; state.Initialize(jsonTypeInfo); return(Deserialize <TValue>( jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase, json, jsonTypeInfo.Options, ref state)); }
private static TValue?Deserialize <TValue>(ReadOnlySpan <char> json, Type returnType, JsonSerializerOptions?options) { if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } options.RootBuiltInConvertersAndTypeInfoCreator(); ReadStack state = default; state.Initialize(returnType, options, supportContinuation: false); JsonConverter jsonConverter = state.Current.JsonPropertyInfo !.ConverterBase; return(Deserialize <TValue>(jsonConverter, json, options, ref state)); }
/// <summary> /// Parse the UTF-8 encoded text representing a single JSON value into a <typeparamref name="TValue"/>. /// </summary> /// <returns>A <typeparamref name="TValue"/> representation of the JSON value.</returns> /// <param name="utf8Json">JSON text to parse.</param> /// <param name="jsonTypeInfo">Metadata about the type to convert.</param> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// <typeparamref name="TValue"/> is not compatible with the JSON, /// or when there is remaining data in the Stream. /// </exception> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <typeparamref name="TValue"/> or its serializable members. /// </exception> public static TValue?Deserialize <TValue>(ReadOnlySpan <byte> utf8Json, JsonTypeInfo <TValue> jsonTypeInfo) { if (jsonTypeInfo == null) { throw new ArgumentNullException(nameof(jsonTypeInfo)); } JsonSerializerOptions options = jsonTypeInfo.Options; var readerState = new JsonReaderState(options.GetReaderOptions()); var reader = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState); ReadStack state = default; state.Initialize(jsonTypeInfo); return(ReadCore <TValue>(jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase, ref reader, options, ref state)); }
/// <summary> /// Reads one JSON value (including objects or arrays) from the provided reader into a <paramref name="returnType"/>. /// </summary> /// <returns>A <paramref name="returnType"/> representation of the JSON value.</returns> /// <param name="reader">The reader to read.</param> /// <param name="returnType">The type of the object to convert to and return.</param> /// <param name="options">Options to control the serializer behavior during reading.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="returnType"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// <paramref name="returnType"/> is not compatible with the JSON, /// or a value could not be read from the reader. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="reader"/> is using unsupported options. /// </exception> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <paramref name="returnType"/> or its serializable members. /// </exception> /// <remarks> /// <para> /// If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/> /// is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the /// reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine /// the start of the value. /// </para> /// /// <para> /// Upon completion of this method <paramref name="reader"/> will be positioned at the /// final token in the JSON value. If an exception is thrown the reader is reset to /// the state it was in when the method was called. /// </para> /// /// <para> /// This method makes a copy of the data the reader acted on, so there is no caller /// requirement to maintain data integrity beyond the return of this method. /// </para> /// <para> /// The <see cref="JsonReaderOptions"/> used to create the instance of the <see cref="Utf8JsonReader"/> take precedence over the <see cref="JsonSerializerOptions"/> when they conflict. /// Hence, <see cref="JsonReaderOptions.AllowTrailingCommas"/>, <see cref="JsonReaderOptions.MaxDepth"/>, <see cref="JsonReaderOptions.CommentHandling"/> are used while reading. /// </para> /// </remarks> public static object?Deserialize(ref Utf8JsonReader reader, [DynamicallyAccessedMembers(MembersAccessedOnRead)] Type returnType, JsonSerializerOptions?options = null) { if (returnType == null) { throw new ArgumentNullException(nameof(returnType)); } if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } ReadStack state = default; state.Initialize(returnType, options, supportContinuation: false); return(ReadValueCore <object>(options, ref reader, ref state)); }
/// <summary> /// Reads one JSON value (including objects or arrays) from the provided reader into a <paramref name="returnType"/>. /// </summary> /// <returns>A <paramref name="returnType"/> representation of the JSON value.</returns> /// <param name="reader">The reader to read.</param> /// <param name="returnType">The type of the object to convert to and return.</param> /// <param name="context">A metadata provider for serializable types.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="returnType"/> or <paramref name="context"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// <paramref name="returnType"/> is not compatible with the JSON, /// or a value could not be read from the reader. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="reader"/> is using unsupported options. /// </exception> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <paramref name="returnType"/> or its serializable members. /// </exception> /// <exception cref="InvalidOperationException"> /// The <see cref="JsonSerializerContext.GetTypeInfo(Type)"/> method on the provided <paramref name="context"/> /// did not return a compatible <see cref="JsonTypeInfo"/> for <paramref name="returnType"/>. /// </exception> /// <remarks> /// <para> /// If the <see cref="Utf8JsonReader.TokenType"/> property of <paramref name="reader"/> /// is <see cref="JsonTokenType.PropertyName"/> or <see cref="JsonTokenType.None"/>, the /// reader will be advanced by one call to <see cref="Utf8JsonReader.Read"/> to determine /// the start of the value. /// </para> /// /// <para> /// Upon completion of this method <paramref name="reader"/> will be positioned at the /// final token in the JSON value. If an exception is thrown the reader is reset to /// the state it was in when the method was called. /// </para> /// /// <para> /// This method makes a copy of the data the reader acted on, so there is no caller /// requirement to maintain data integrity beyond the return of this method. /// </para> /// <para> /// The <see cref="JsonReaderOptions"/> used to create the instance of the <see cref="Utf8JsonReader"/> take precedence over the <see cref="JsonSerializerOptions"/> when they conflict. /// Hence, <see cref="JsonReaderOptions.AllowTrailingCommas"/>, <see cref="JsonReaderOptions.MaxDepth"/>, <see cref="JsonReaderOptions.CommentHandling"/> are used while reading. /// </para> /// </remarks> public static object?Deserialize(ref Utf8JsonReader reader, Type returnType, JsonSerializerContext context) { if (returnType == null) { throw new ArgumentNullException(nameof(returnType)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } ReadStack state = default; JsonTypeInfo jsonTypeInfo = JsonHelpers.GetTypeInfo(context, returnType); state.Initialize(jsonTypeInfo); return(ReadValueCore <object>(jsonTypeInfo.Options, ref reader, ref state)); }
/// <summary> /// Parse the UTF-8 encoded text representing a single JSON value into a <paramref name="returnType"/>. /// </summary> /// <returns>A <paramref name="returnType"/> representation of the JSON value.</returns> /// <param name="utf8Json">JSON text to parse.</param> /// <param name="returnType">The type of the object to convert to and return.</param> /// <param name="context">A metadata provider for serializable types.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="returnType"/> is <see langword="null"/>. /// </exception> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// <paramref name="returnType"/> is not compatible with the JSON, /// or when there is remaining data in the Stream. /// </exception> /// <exception cref="NotSupportedException"> /// There is no compatible <see cref="System.Text.Json.Serialization.JsonConverter"/> /// for <paramref name="returnType"/> or its serializable members. /// </exception> /// <exception cref="InvalidOperationException"> /// The <see cref="JsonSerializerContext.GetTypeInfo(Type)"/> method on the provided <paramref name="context"/> /// did not return a compatible <see cref="JsonTypeInfo"/> for <paramref name="returnType"/>. /// </exception> public static object?Deserialize(ReadOnlySpan <byte> utf8Json, Type returnType, JsonSerializerContext context) { if (returnType == null) { throw new ArgumentNullException(nameof(returnType)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } JsonTypeInfo jsonTypeInfo = JsonHelpers.GetTypeInfo(context, returnType); JsonSerializerOptions options = jsonTypeInfo.Options; var readerState = new JsonReaderState(options.GetReaderOptions()); var reader = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState); ReadStack state = default; state.Initialize(jsonTypeInfo); return(ReadCore <object?>(jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase, ref reader, options, ref state)); }
private static async ValueTask <TValue> ReadAsync <TValue>( Stream utf8Json, Type returnType, JsonSerializerOptions?options, CancellationToken cancellationToken) { if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } ReadStack state = default; state.Initialize(returnType, options, supportContinuation: true); JsonConverter converter = state.Current.JsonPropertyInfo !.ConverterBase; var readerState = new JsonReaderState(options.GetReaderOptions()); // todo: https://github.com/dotnet/runtime/issues/32355 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 TValue value = ReadCore <TValue>( ref readerState, isFinalBlock, new ReadOnlySpan <byte>(buffer, start, bytesInBuffer), options, ref state, converter); Debug.Assert(state.BytesConsumed <= bytesInBuffer); int bytesConsumed = checked ((int)state.BytesConsumed); bytesInBuffer -= bytesConsumed; if (isFinalBlock) { // The reader should have thrown if we have remaining bytes. Debug.Assert(bytesInBuffer == 0); return(value); } // 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); } }
private static TValue?ReadUsingMetadata <TValue>(ref Utf8JsonReader reader, JsonTypeInfo jsonTypeInfo) { ReadStack state = default; state.Initialize(jsonTypeInfo); JsonReaderState readerState = reader.CurrentState; if (readerState.Options.CommentHandling == JsonCommentHandling.Allow) { throw new ArgumentException(SR.JsonSerializerDoesNotSupportComments, nameof(reader)); } // Value copy to overwrite the ref on an exception and undo the destructive reads. Utf8JsonReader restore = reader; ReadOnlySpan <byte> valueSpan = default; ReadOnlySequence <byte> valueSequence = default; try { switch (reader.TokenType) { // A new reader was created and has never been read, // so we need to move to the first token. // (or a reader has terminated and we're about to throw) case JsonTokenType.None: // Using a reader loop the caller has identified a property they wish to // hydrate into a JsonDocument. Move to the value first. case JsonTokenType.PropertyName: { if (!reader.Read()) { ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.ExpectedOneCompleteToken); } break; } } switch (reader.TokenType) { // Any of the "value start" states are acceptable. case JsonTokenType.StartObject: case JsonTokenType.StartArray: { long startingOffset = reader.TokenStartIndex; if (!reader.TrySkip()) { ThrowHelper.ThrowJsonReaderException(ref reader, ExceptionResource.NotEnoughData); } long totalLength = reader.BytesConsumed - startingOffset; ReadOnlySequence <byte> sequence = reader.OriginalSequence; if (sequence.IsEmpty) { valueSpan = reader.OriginalSpan.Slice( checked ((int)startingOffset), checked ((int)totalLength)); } else { valueSequence = sequence.Slice(startingOffset, totalLength); } Debug.Assert( reader.TokenType == JsonTokenType.EndObject || reader.TokenType == JsonTokenType.EndArray); break; } // Single-token values case JsonTokenType.Number: case JsonTokenType.True: case JsonTokenType.False: case JsonTokenType.Null: { if (reader.HasValueSequence) { valueSequence = reader.ValueSequence; } else { valueSpan = reader.ValueSpan; } break; } // String's ValueSequence/ValueSpan omits the quotes, we need them back. case JsonTokenType.String: { ReadOnlySequence <byte> sequence = reader.OriginalSequence; if (sequence.IsEmpty) { // Since the quoted string fit in a ReadOnlySpan originally // the contents length plus the two quotes can't overflow. int payloadLength = reader.ValueSpan.Length + 2; Debug.Assert(payloadLength > 1); ReadOnlySpan <byte> readerSpan = reader.OriginalSpan; Debug.Assert( readerSpan[(int)reader.TokenStartIndex] == (byte)'"', $"Calculated span starts with {readerSpan[(int)reader.TokenStartIndex]}"); Debug.Assert( readerSpan[(int)reader.TokenStartIndex + payloadLength - 1] == (byte)'"', $"Calculated span ends with {readerSpan[(int)reader.TokenStartIndex + payloadLength - 1]}"); valueSpan = readerSpan.Slice((int)reader.TokenStartIndex, payloadLength); } else { long payloadLength = 2; if (reader.HasValueSequence) { payloadLength += reader.ValueSequence.Length; } else { payloadLength += reader.ValueSpan.Length; } valueSequence = sequence.Slice(reader.TokenStartIndex, payloadLength); Debug.Assert( valueSequence.First.Span[0] == (byte)'"', $"Calculated sequence starts with {valueSequence.First.Span[0]}"); Debug.Assert( valueSequence.ToArray()[payloadLength - 1] == (byte)'"', $"Calculated sequence ends with {valueSequence.ToArray()[payloadLength - 1]}"); } break; } default: { byte displayByte; if (reader.HasValueSequence) { displayByte = reader.ValueSequence.First.Span[0]; } else { displayByte = reader.ValueSpan[0]; } ThrowHelper.ThrowJsonReaderException( ref reader, ExceptionResource.ExpectedStartOfValueNotFound, displayByte); break; } } } catch (JsonReaderException ex) { reader = restore; // Re-throw with Path information. ThrowHelper.ReThrowWithPath(state, ex); } int length = valueSpan.IsEmpty ? checked ((int)valueSequence.Length) : valueSpan.Length; byte[] rented = ArrayPool <byte> .Shared.Rent(length); Span <byte> rentedSpan = rented.AsSpan(0, length); try { if (valueSpan.IsEmpty) { valueSequence.CopyTo(rentedSpan); } else { valueSpan.CopyTo(rentedSpan); } JsonReaderOptions originalReaderOptions = readerState.Options; var newReader = new Utf8JsonReader(rentedSpan, originalReaderOptions); JsonConverter jsonConverter = state.Current.JsonPropertyInfo !.ConverterBase; TValue? value = ReadCore <TValue>(jsonConverter, ref newReader, jsonTypeInfo.Options, ref state); // The reader should have thrown if we have remaining bytes. Debug.Assert(newReader.BytesConsumed == length); return(value); } catch (JsonException) { reader = restore; throw; } finally { rentedSpan.Clear(); ArrayPool <byte> .Shared.Return(rented); } }