internal static JsonDocument ParseValue(ReadOnlyMemory <char> json, JsonDocumentOptions options) { ReadOnlySpan <char> jsonChars = json.Span; int expectedByteCount = JsonReaderHelper.GetUtf8ByteCount(jsonChars); byte[] owned; byte[] utf8Bytes = ArrayPool <byte> .Shared.Rent(expectedByteCount); try { int actualByteCount = JsonReaderHelper.GetUtf8FromText(jsonChars, utf8Bytes); Debug.Assert(expectedByteCount == actualByteCount); owned = new byte[actualByteCount]; Buffer.BlockCopy(utf8Bytes, 0, owned, 0, actualByteCount); } finally { // Holds document content, clear it before returning it. utf8Bytes.AsSpan(0, expectedByteCount).Clear(); ArrayPool <byte> .Shared.Return(utf8Bytes); } return(ParseUnrented(owned.AsMemory(), options.GetReaderOptions())); }
public static bool TryParseAsISO(ReadOnlySpan <char> source, out DateTimeOffset value) { if (!IsValidDateTimeOffsetParseLength(source.Length)) { value = default; return(false); } int maxLength = checked (source.Length * JsonConstants.MaxExpansionFactorWhileTranscoding); Span <byte> bytes = maxLength <= JsonConstants.StackallocThreshold ? stackalloc byte[JsonConstants.StackallocThreshold] : new byte[maxLength]; int length = JsonReaderHelper.GetUtf8FromText(source, bytes); bytes = bytes.Slice(0, length); if (bytes.IndexOf(JsonConstants.BackSlash) != -1) { return(JsonReaderHelper.TryGetEscapedDateTimeOffset(bytes, out value)); } Debug.Assert(bytes.IndexOf(JsonConstants.BackSlash) == -1); if (TryParseAsISO(bytes, out DateTimeOffset tmp)) { value = tmp; return(true); } value = default; return(false); }
/// <summary> /// Parses text representing a single JSON value into a JsonDocument. /// </summary> /// <remarks> /// The <see cref="ReadOnlyMemory{T}"/> value may be used for the entire lifetime of the /// JsonDocument object, and the caller must ensure that the data therein does not change during /// the object lifetime. /// </remarks> /// <param name="json">JSON text to parse.</param> /// <param name="options">Options to control the reader behavior during parsing.</param> /// <returns> /// A JsonDocument representation of the JSON value. /// </returns> /// <exception cref="JsonException"> /// <paramref name="json"/> does not represent a valid single JSON value. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="options"/> contains unsupported options. /// </exception> public static JsonDocument Parse([StringSyntax(StringSyntaxAttribute.Json)] ReadOnlyMemory <char> json, JsonDocumentOptions options = default) { ReadOnlySpan <char> jsonChars = json.Span; int expectedByteCount = JsonReaderHelper.GetUtf8ByteCount(jsonChars); byte[] utf8Bytes = ArrayPool <byte> .Shared.Rent(expectedByteCount); try { int actualByteCount = JsonReaderHelper.GetUtf8FromText(jsonChars, utf8Bytes); Debug.Assert(expectedByteCount == actualByteCount); return(Parse( utf8Bytes.AsMemory(0, actualByteCount), options.GetReaderOptions(), utf8Bytes)); } catch { // Holds document content, clear it before returning it. utf8Bytes.AsSpan(0, expectedByteCount).Clear(); ArrayPool <byte> .Shared.Return(utf8Bytes); throw; } }
private static TValue?ReadUsingMetadata <TValue>(ReadOnlySpan <char> json, JsonTypeInfo jsonTypeInfo) { const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = 1024 * 1024; byte[]? tempArray = null; // For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold. Span <byte> utf8 = json.Length <= (ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ? // Use a pooled alloc. tempArray = ArrayPool <byte> .Shared.Rent(json.Length *JsonConstants.MaxExpansionFactorWhileTranscoding) : // Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation) // and by using a normal alloc we can avoid the Clear(). new byte[JsonReaderHelper.GetUtf8ByteCount(json)]; try { int actualByteCount = JsonReaderHelper.GetUtf8FromText(json, utf8); utf8 = utf8.Slice(0, actualByteCount); return(ReadUsingMetadata <TValue>(utf8, jsonTypeInfo, actualByteCount)); } finally { if (tempArray != null) { utf8.Clear(); ArrayPool <byte> .Shared.Return(tempArray); } } }
private void TranscodeAndWriteRawValue(ReadOnlySpan <char> json, bool skipInputValidation) { if (json.Length > JsonConstants.MaxUtf16RawValueLength) { ThrowHelper.ThrowArgumentException_ValueTooLarge(json.Length); } byte[]? tempArray = null; // For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold. Span <byte> utf8Json = json.Length <= (JsonConstants.ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ? // Use a pooled alloc. tempArray = ArrayPool <byte> .Shared.Rent(json.Length *JsonConstants.MaxExpansionFactorWhileTranscoding) : // Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation) // and by using a normal alloc we can avoid the Clear(). new byte[JsonReaderHelper.GetUtf8ByteCount(json)]; try { int actualByteCount = JsonReaderHelper.GetUtf8FromText(json, utf8Json); utf8Json = utf8Json.Slice(0, actualByteCount); WriteRawValueCore(utf8Json, skipInputValidation); } finally { if (tempArray != null) { utf8Json.Clear(); ArrayPool <byte> .Shared.Return(tempArray); } } }
/// <summary> /// Parse the 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="json">JSON text to parse.</param> /// <param name="returnType">The type of the object to convert to and return.</param> /// <param name="options">Options to control the behavior during parsing.</param> /// <exception cref="System.ArgumentNullException"> /// Thrown if <paramref name="json"/> or <paramref name="returnType"/> is null. /// </exception> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// the <paramref name="returnType"/> is not compatible with the JSON, /// or when there is remaining data in the Stream. /// </exception> /// <remarks>Using a <see cref="System.String"/> is not as efficient as using the /// UTF-8 methods since the implementation natively uses UTF-8. /// </remarks> public static object Deserialize(string json, Type returnType, JsonSerializerOptions options = null) { const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = 1024 * 1024; if (json == null) { throw new ArgumentNullException(nameof(json)); } if (returnType == null) { throw new ArgumentNullException(nameof(returnType)); } if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } object result; byte[] tempArray = null; // For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold. Span <byte> utf8 = json.Length <= (ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ? // Use a pooled alloc. tempArray = ArrayPool <byte> .Shared.Rent(json.Length *JsonConstants.MaxExpansionFactorWhileTranscoding) : // Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation) // and by using a normal alloc we can avoid the Clear(). new byte[JsonReaderHelper.GetUtf8ByteCount(json.AsSpan())]; try { int actualByteCount = JsonReaderHelper.GetUtf8FromText(json.AsSpan(), utf8); utf8 = utf8.Slice(0, actualByteCount); var readerState = new JsonReaderState(options.GetReaderOptions()); var reader = new Utf8JsonReader(utf8, isFinalBlock: true, readerState); result = ReadCore(returnType, options, ref reader); // The reader should have thrown if we have remaining bytes. Debug.Assert(reader.BytesConsumed == actualByteCount); } finally { if (tempArray != null) { utf8.Clear(); ArrayPool <byte> .Shared.Return(tempArray); } } return(result); }
public static bool TryParseAsISO(ReadOnlySpan <char> source, out DateTimeOffset value) { if (!IsValidDateTimeOffsetParseLength(source.Length)) { value = default; return(false); } int maxLength = checked (source.Length * JsonConstants.MaxExpansionFactorWhileTranscoding); Span <byte> bytes = maxLength <= JsonConstants.StackallocThreshold ? stackalloc byte[JsonConstants.StackallocThreshold] : new byte[maxLength]; int length = JsonReaderHelper.GetUtf8FromText(source, bytes); return(TryParseAsISO(bytes.Slice(0, length), out value)); }
public static bool TryParseAsISO(ReadOnlySpan <char> source, out DateTimeOffset value) { if (!IsValidDateTimeOffsetParseLength(source.Length)) { value = default; return(false); } int length = JsonReaderHelper.GetUtf8ByteCount(source); Span <byte> bytes = length <= JsonConstants.StackallocThreshold ? stackalloc byte[JsonConstants.StackallocThreshold] : new byte[length]; JsonReaderHelper.GetUtf8FromText(source, bytes); return(TryParseAsISO(bytes.Slice(0, length), out value)); }
private static TValue?Deserialize <TValue>( JsonConverter jsonConverter, ReadOnlySpan <char> json, JsonSerializerOptions options, ref ReadStack state) { const long ArrayPoolMaxSizeBeforeUsingNormalAlloc = 1024 * 1024; byte[]? tempArray = null; // For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold. Span <byte> utf8 = json.Length <= (ArrayPoolMaxSizeBeforeUsingNormalAlloc / JsonConstants.MaxExpansionFactorWhileTranscoding) ? // Use a pooled alloc. tempArray = ArrayPool <byte> .Shared.Rent(json.Length *JsonConstants.MaxExpansionFactorWhileTranscoding) : // Use a normal alloc since the pool would create a normal alloc anyway based on the threshold (per current implementation) // and by using a normal alloc we can avoid the Clear(). new byte[JsonReaderHelper.GetUtf8ByteCount(json)]; try { int actualByteCount = JsonReaderHelper.GetUtf8FromText(json, utf8); utf8 = utf8.Slice(0, actualByteCount); var readerState = new JsonReaderState(options.GetReaderOptions()); var reader = new Utf8JsonReader(utf8, isFinalBlock: true, readerState); TValue?value = ReadCore <TValue>(jsonConverter, ref reader, options, ref state); // The reader should have thrown if we have remaining bytes. Debug.Assert(reader.BytesConsumed == actualByteCount); return(value); } finally { if (tempArray != null) { utf8.Clear(); ArrayPool <byte> .Shared.Return(tempArray); } } }
private static JsonEncodedText TranscodeAndEncode(ReadOnlySpan <char> value) { JsonWriterHelper.ValidateValue(value); int expectedByteCount = JsonReaderHelper.GetUtf8ByteCount(value); byte[] utf8Bytes = ArrayPool <byte> .Shared.Rent(expectedByteCount); JsonEncodedText encodedText; // Since GetUtf8ByteCount above already throws on invalid input, the transcoding // to UTF-8 is guaranteed to succeed here. Therefore, there's no need for a try-catch-finally block. int actualByteCount = JsonReaderHelper.GetUtf8FromText(value, utf8Bytes); Debug.Assert(expectedByteCount == actualByteCount); encodedText = EncodeHelper(utf8Bytes.AsSpan(0, actualByteCount)); // On the basis that this is user data, go ahead and clear it. utf8Bytes.AsSpan(0, expectedByteCount).Clear(); ArrayPool <byte> .Shared.Return(utf8Bytes); return(encodedText); }
internal bool TryGetNamedPropertyValue(int index, ReadOnlySpan <char> propertyName, out JsonElement value) { CheckNotDisposed(); DbRow row = _parsedData.Get(index); CheckExpectedType(JsonTokenType.StartObject, row.TokenType); // Only one row means it was EndObject. if (row.NumberOfRows == 1) { value = default; return(false); } int maxBytes = JsonReaderHelper.s_utf8Encoding.GetMaxByteCount(propertyName.Length); int startIndex = index + DbRow.Size; int endIndex = checked (row.NumberOfRows * DbRow.Size + index); if (maxBytes < JsonConstants.StackallocThreshold) { Span <byte> utf8Name = stackalloc byte[JsonConstants.StackallocThreshold]; int len = JsonReaderHelper.GetUtf8FromText(propertyName, utf8Name); utf8Name = utf8Name.Slice(0, len); return(TryGetNamedPropertyValue( startIndex, endIndex, utf8Name, out value)); } // Unescaping the property name will make the string shorter (or the same) // So the first viable candidate is one whose length in bytes matches, or // exceeds, our length in chars. // // The maximal escaping seems to be 6 -> 1 ("\u0030" => "0"), but just transcode // and switch once one viable long property is found. int minBytes = propertyName.Length; // Move to the row before the EndObject int candidateIndex = endIndex - DbRow.Size; while (candidateIndex > index) { int passedIndex = candidateIndex; row = _parsedData.Get(candidateIndex); Debug.Assert(row.TokenType != JsonTokenType.PropertyName); // Move before the value if (row.IsSimpleValue) { candidateIndex -= DbRow.Size; } else { Debug.Assert(row.NumberOfRows > 0); candidateIndex -= DbRow.Size * (row.NumberOfRows + 1); } row = _parsedData.Get(candidateIndex); Debug.Assert(row.TokenType == JsonTokenType.PropertyName); if (row.SizeOrLength >= minBytes) { byte[] tmpUtf8 = ArrayPool <byte> .Shared.Rent(maxBytes); Span <byte> utf8Name = default; try { int len = JsonReaderHelper.GetUtf8FromText(propertyName, tmpUtf8); utf8Name = tmpUtf8.AsSpan(0, len); return(TryGetNamedPropertyValue( startIndex, passedIndex + DbRow.Size, utf8Name, out value)); } finally { // While property names aren't usually a secret, they also usually // aren't long enough to end up in the rented buffer transcode path. // // On the basis that this is user data, go ahead and clear it. utf8Name.Clear(); ArrayPool <byte> .Shared.Return(tmpUtf8); } } // Move to the previous value candidateIndex -= DbRow.Size; } // None of the property names were within the range that the UTF-8 encoding would have been. value = default; return(false); }
/// <summary> /// Parse the 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="json">JSON text to parse.</param> /// <param name="returnType">The type of the object to convert to and return.</param> /// <param name="options">Options to control the behavior during parsing.</param> /// <exception cref="System.ArgumentNullException"> /// Thrown if <paramref name="json"/> or <paramref name="returnType"/> is null. /// </exception> /// <exception cref="JsonException"> /// Thrown when the JSON is invalid, /// the <paramref name="returnType"/> is not compatible with the JSON, /// or when there is remaining data in the Stream. /// </exception> /// <remarks>Using a <see cref="string"/> is not as efficient as using the /// UTF-8 methods since the implementation natively uses UTF-8. /// </remarks> public static object Deserialize(string json, Type returnType, JsonSerializerOptions options = null) { if (json == null) { throw new ArgumentNullException(nameof(json)); } if (returnType == null) { throw new ArgumentNullException(nameof(returnType)); } if (options == null) { options = JsonSerializerOptions.s_defaultOptions; } object result; byte[] tempArray = null; int maxBytes; // For performance, avoid asking for the actual byte count unless necessary. if (json.Length > MaxArrayLengthBeforeCalculatingSize) { // Get the actual byte count in order to handle large input. maxBytes = JsonReaderHelper.GetUtf8ByteCount(json.AsSpan()); } else { maxBytes = json.Length * JsonConstants.MaxExpansionFactorWhileTranscoding; } Span <byte> utf8 = maxBytes <= JsonConstants.StackallocThreshold ? stackalloc byte[maxBytes] : (tempArray = ArrayPool <byte> .Shared.Rent(maxBytes)); try { int actualByteCount = JsonReaderHelper.GetUtf8FromText(json.AsSpan(), utf8); utf8 = utf8.Slice(0, actualByteCount); var readerState = new JsonReaderState(options.GetReaderOptions()); var reader = new Utf8JsonReader(utf8, isFinalBlock: true, readerState); result = ReadCore(returnType, options, ref reader); if (reader.BytesConsumed != actualByteCount) { ThrowHelper.ThrowJsonException_DeserializeDataRemaining( actualByteCount, actualByteCount - reader.BytesConsumed); } } finally { if (tempArray != null) { utf8.Clear(); ArrayPool <byte> .Shared.Return(tempArray); } } return(result); }