Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<Compile Include="System\Text\Json\JsonException.cs" />
<Compile Include="System\Text\Json\JsonHelpers.cs" />
<Compile Include="System\Text\Json\JsonHelpers.Date.cs" />
<Compile Include="System\Text\Json\JsonHelpers.Escaping.cs" />
<Compile Include="System\Text\Json\JsonTokenType.cs" />
<Compile Include="System\Text\Json\Reader\ConsumeNumberResult.cs" />
<Compile Include="System\Text\Json\Reader\ConsumeTokenResult.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,6 @@ private JsonEncodedText(byte[] utf8Value)
_utf8Value = utf8Value;
}

private JsonEncodedText(string stringValue, byte[] utf8Value)
{
Debug.Assert(stringValue != null);
Debug.Assert(utf8Value != null);

_value = stringValue;
_utf8Value = utf8Value;
}

/// <summary>
/// Encodes the string text value as a JSON string.
/// </summary>
Expand Down Expand Up @@ -126,70 +117,14 @@ private static JsonEncodedText EncodeHelper(ReadOnlySpan<byte> utf8Value, JavaSc

if (idx != -1)
{
return new JsonEncodedText(GetEscapedString(utf8Value, idx, encoder));
return new JsonEncodedText(JsonHelpers.EscapeValue(utf8Value, idx, encoder));
}
else
{
return new JsonEncodedText(utf8Value.ToArray());
}
}

/// <summary>
/// Internal version that keeps the existing string and byte[] references if there is no escaping required.
/// </summary>
internal static JsonEncodedText Encode(string stringValue, byte[] utf8Value, JavaScriptEncoder? encoder = null)
{
Debug.Assert(stringValue.Equals(JsonHelpers.Utf8GetString(utf8Value)));

if (utf8Value.Length == 0)
{
return new JsonEncodedText(stringValue, utf8Value);
}

JsonWriterHelper.ValidateValue(utf8Value);
return EncodeHelper(stringValue, utf8Value, encoder);
}

private static JsonEncodedText EncodeHelper(string stringValue, byte[] utf8Value, JavaScriptEncoder? encoder)
{
int idx = JsonWriterHelper.NeedsEscaping(utf8Value, encoder);

if (idx != -1)
{
return new JsonEncodedText(GetEscapedString(utf8Value, idx, encoder));
}
else
{
// Encoding is not necessary; use the same stringValue and utf8Value references.
return new JsonEncodedText(stringValue, utf8Value);
}
}

private static byte[] GetEscapedString(ReadOnlySpan<byte> utf8Value, int firstEscapeIndexVal, JavaScriptEncoder? encoder)
{
Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= utf8Value.Length);
Debug.Assert(firstEscapeIndexVal >= 0 && firstEscapeIndexVal < utf8Value.Length);

byte[]? valueArray = null;

int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);

Span<byte> escapedValue = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
(valueArray = ArrayPool<byte>.Shared.Rent(length));

JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, encoder, out int written);

byte[] escapedString = escapedValue.Slice(0, written).ToArray();

if (valueArray != null)
{
ArrayPool<byte>.Shared.Return(valueArray);
}

return escapedString;
}

/// <summary>
/// Determines whether this instance and another specified <see cref="JsonEncodedText"/> instance have the same value.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text.Encodings.Web;

namespace System.Text.Json
{
internal static partial class JsonHelpers
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] GetEscapedPropertyNameSection(ReadOnlySpan<byte> utf8Value, JavaScriptEncoder? encoder)
{
int idx = JsonWriterHelper.NeedsEscaping(utf8Value, encoder);

if (idx != -1)
{
return GetEscapedPropertyNameSection(utf8Value, idx, encoder);
}
else
{
return GetPropertyNameSection(utf8Value);
}
}

public static byte[] EscapeValue(
ReadOnlySpan<byte> utf8Value,
int firstEscapeIndexVal,
JavaScriptEncoder? encoder)
{
Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= utf8Value.Length);
Debug.Assert(firstEscapeIndexVal >= 0 && firstEscapeIndexVal < utf8Value.Length);

byte[]? valueArray = null;

int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);

Span<byte> escapedValue = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
(valueArray = ArrayPool<byte>.Shared.Rent(length));

JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, encoder, out int written);

byte[] escapedString = escapedValue.Slice(0, written).ToArray();

if (valueArray != null)
{
ArrayPool<byte>.Shared.Return(valueArray);
}

return escapedString;
}

private static byte[] GetEscapedPropertyNameSection(
ReadOnlySpan<byte> utf8Value,
int firstEscapeIndexVal,
JavaScriptEncoder? encoder)
{
Debug.Assert(int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping >= utf8Value.Length);
Debug.Assert(firstEscapeIndexVal >= 0 && firstEscapeIndexVal < utf8Value.Length);

byte[]? valueArray = null;

int length = JsonWriterHelper.GetMaxEscapedLength(utf8Value.Length, firstEscapeIndexVal);

Span<byte> escapedValue = length <= JsonConstants.StackallocThreshold ?
stackalloc byte[length] :
(valueArray = ArrayPool<byte>.Shared.Rent(length));

JsonWriterHelper.EscapeString(utf8Value, escapedValue, firstEscapeIndexVal, encoder, out int written);

byte[] propertySection = GetPropertyNameSection(escapedValue.Slice(0, written));

if (valueArray != null)
{
ArrayPool<byte>.Shared.Return(valueArray);
}

return propertySection;
}

private static byte[] GetPropertyNameSection(ReadOnlySpan<byte> utf8Value)
{
int length = utf8Value.Length;
byte[] propertySection = new byte[length + 3];

propertySection[0] = JsonConstants.Quote;
utf8Value.CopyTo(propertySection.AsSpan(1, length));
propertySection[++length] = JsonConstants.Quote;
propertySection[++length] = JsonConstants.KeyValueSeperator;

return propertySection;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static void ReadWithVerify(this ref Utf8JsonReader reader)
/// </summary>
/// <param name="bytes">The utf8 bytes to convert.</param>
/// <returns></returns>
internal static string Utf8GetString(ReadOnlySpan<byte> bytes)
public static string Utf8GetString(ReadOnlySpan<byte> bytes)
{
return Encoding.UTF8.GetString(bytes
#if NETSTANDARD2_0 || NETFRAMEWORK
Expand All @@ -103,7 +103,7 @@ internal static string Utf8GetString(ReadOnlySpan<byte> bytes)
/// <summary>
/// Emulates Dictionary.TryAdd on netstandard.
/// </summary>
internal static bool TryAdd<TKey, TValue>(Dictionary<TKey, TValue> dictionary, TKey key, TValue value) where TKey : notnull
public static bool TryAdd<TKey, TValue>(Dictionary<TKey, TValue> dictionary, in TKey key, in TValue value) where TKey : notnull
{
#if NETSTANDARD2_0 || NETFRAMEWORK
if (!dictionary.ContainsKey(key))
Expand All @@ -118,7 +118,7 @@ internal static bool TryAdd<TKey, TValue>(Dictionary<TKey, TValue> dictionary, T
#endif
}

internal static bool IsFinite(double value)
public static bool IsFinite(double value)
{
#if BUILDING_INBOX_LIBRARY
return double.IsFinite(value);
Expand All @@ -127,7 +127,7 @@ internal static bool IsFinite(double value)
#endif
}

internal static bool IsFinite(float value)
public static bool IsFinite(float value)
{
#if BUILDING_INBOX_LIBRARY
return float.IsFinite(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal sealed class ArrayConverter<TCollection, TElement>
{
internal override bool CanHaveIdMetadata => false;

protected override void Add(TElement value, ref ReadStack state)
protected override void Add(in TElement value, ref ReadStack state)
{
((List<TElement>)state.Current.ReturnValue!).Add(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal sealed class ConcurrentQueueOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : ConcurrentQueue<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add(in TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Enqueue(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal sealed class ConcurrentStackOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : ConcurrentStack<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add(in TElement value, ref ReadStack state)
{
((TCollection)state.Current.ReturnValue!).Push(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal abstract class DictionaryDefaultConverter<TCollection, TValue>
/// <summary>
/// When overridden, adds the value to the collection.
/// </summary>
protected abstract void Add(TValue value, JsonSerializerOptions options, ref ReadStack state);
protected abstract void Add(in TValue value, JsonSerializerOptions options, ref ReadStack state);

/// <summary>
/// When overridden, converts the temporary collection held in state.Current.ReturnValue to the final collection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal sealed class DictionaryOfStringTValueConverter<TCollection, TValue>
: DictionaryDefaultConverter<TCollection, TValue>
where TCollection : Dictionary<string, TValue>
{
protected override void Add(TValue value, JsonSerializerOptions options, ref ReadStack state)
protected override void Add(in TValue value, JsonSerializerOptions options, ref ReadStack state)
{
string key = state.Current.JsonPropertyNameAsString!;
((TCollection)state.Current.ReturnValue!)[key] = value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal sealed class ICollectionOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : ICollection<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add(in TElement value, ref ReadStack state)
{
((ICollection<TElement>)state.Current.ReturnValue!).Add(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal sealed class IDictionaryConverter<TCollection>
: DictionaryDefaultConverter<TCollection, object?>
where TCollection : IDictionary
{
protected override void Add(object? value, JsonSerializerOptions options, ref ReadStack state)
protected override void Add(in object? value, JsonSerializerOptions options, ref ReadStack state)
{
string key = state.Current.JsonPropertyNameAsString!;
((IDictionary)state.Current.ReturnValue!)[key] = value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal sealed class IDictionaryOfStringTValueConverter<TCollection, TValue>
: DictionaryDefaultConverter<TCollection, TValue>
where TCollection : IDictionary<string, TValue>
{
protected override void Add(TValue value, JsonSerializerOptions options, ref ReadStack state)
protected override void Add(in TValue value, JsonSerializerOptions options, ref ReadStack state)
{
string key = state.Current.JsonPropertyNameAsString!;
((TCollection)state.Current.ReturnValue!)[key] = value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal sealed class IEnumerableConverter<TCollection>
: IEnumerableDefaultConverter<TCollection, object?>
where TCollection : IEnumerable
{
protected override void Add(object? value, ref ReadStack state)
protected override void Add(in object? value, ref ReadStack state)
{
((List<object?>)state.Current.ReturnValue!).Add(value);
}
Expand Down Expand Up @@ -62,7 +62,8 @@ protected override bool OnWriteResume(
return false;
}

if (!converter.TryWrite(writer, enumerator.Current, options, ref state))
object? element = enumerator.Current;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note this is for consistency with other code. Also applies to another case of this later.

if (!converter.TryWrite(writer, element, options, ref state))
{
state.Current.CollectionEnumerator = enumerator;
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ internal static class IEnumerableConverterFactoryHelpers

Type? baseTypeToCheck = type;

while (baseTypeToCheck != null && baseTypeToCheck != typeof(object))
while (baseTypeToCheck != null && baseTypeToCheck != JsonClassInfo.ObjectType)
{
if (baseTypeToCheck.IsGenericType)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace System.Text.Json.Serialization.Converters
internal abstract class IEnumerableDefaultConverter<TCollection, TElement>
: JsonCollectionConverter<TCollection, TElement>
{
protected abstract void Add(TElement value, ref ReadStack state);
protected abstract void Add(in TElement value, ref ReadStack state);
protected abstract void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options);
protected virtual void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { }

Expand Down Expand Up @@ -242,7 +242,11 @@ internal override bool OnTryRead(
return true;
}

internal sealed override bool OnTryWrite(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state)
internal sealed override bool OnTryWrite(
Utf8JsonWriter writer,
TCollection value,
JsonSerializerOptions options,
ref WriteStack state)
{
bool success;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal sealed class IEnumerableOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : IEnumerable<TElement>
{
protected override void Add(TElement value, ref ReadStack state)
protected override void Add(in TElement value, ref ReadStack state)
{
((List<TElement>)state.Current.ReturnValue!).Add(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ internal sealed class IEnumerableWithAddMethodConverter<TCollection>
: IEnumerableDefaultConverter<TCollection, object?>
where TCollection : IEnumerable
{
protected override void Add(object? value, ref ReadStack state)
protected override void Add(in object? value, ref ReadStack state)
{
((Action<TCollection, object?>)state.Current.AddMethodDelegate!)((TCollection)state.Current.ReturnValue!, value);
}
Expand Down Expand Up @@ -53,7 +53,8 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value,
return false;
}

if (!converter.TryWrite(writer, enumerator.Current, options, ref state))
object? element = enumerator.Current;
if (!converter.TryWrite(writer, element, options, ref state))
{
state.Current.CollectionEnumerator = enumerator;
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal sealed class IListConverter<TCollection>
: IEnumerableDefaultConverter<TCollection, object?>
where TCollection : IList
{
protected override void Add(object? value, ref ReadStack state)
protected override void Add(in object? value, ref ReadStack state)
{
((IList)state.Current.ReturnValue!).Add(value);
}
Expand Down Expand Up @@ -74,7 +74,6 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value,
}

object? element = enumerator.Current;

if (!converter.TryWrite(writer, element, options, ref state))
{
state.Current.CollectionEnumerator = enumerator;
Expand Down
Loading