Skip to content

Commit b694e22

Browse files
committed
Performance improvements
1 parent 23f5d46 commit b694e22

File tree

3 files changed

+47
-38
lines changed

3 files changed

+47
-38
lines changed

src/libraries/System.Text.Json/Common/JsonConstants.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ internal static partial class JsonConstants
88
// Standard format for double and single on non-inbox frameworks.
99
public const string DoubleFormatString = "G17";
1010
public const string SingleFormatString = "G9";
11+
12+
public const int StackallocByteThreshold = 256;
13+
public const int StackallocCharThreshold = StackallocByteThreshold / 2;
1114
}
1215
}

src/libraries/System.Text.Json/Common/JsonSeparatorNamingPolicy.cs

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,56 @@ internal JsonSeparatorNamingPolicy(bool lowercase, char separator) =>
1616

1717
public override string ConvertName(string name)
1818
{
19-
int bufferLength = name.Length * 2;
20-
char[]? buffer = bufferLength > 512
19+
// Rented buffer 20% longer that the input.
20+
int bufferLength = (12 * name.Length) / 10;
21+
char[]? buffer = bufferLength > JsonConstants.StackallocCharThreshold
2122
? ArrayPool<char>.Shared.Rent(bufferLength)
2223
: null;
2324

2425
int resultLength = 0;
2526
Span<char> result = buffer is null
26-
? stackalloc char[512]
27+
? stackalloc char[JsonConstants.StackallocCharThreshold]
2728
: buffer;
2829

29-
void WriteWord(ref Span<char> result, ReadOnlySpan<char> word)
30+
void ExpandBuffer(ref Span<char> result)
31+
{
32+
var bufferNew = ArrayPool<char>.Shared.Rent(bufferLength *= 2);
33+
34+
result.CopyTo(bufferNew);
35+
36+
if (buffer is not null)
37+
{
38+
ArrayPool<char>.Shared.Return(buffer, clearArray: true);
39+
}
40+
41+
buffer = bufferNew;
42+
result = buffer;
43+
}
44+
45+
void WriteWord(ReadOnlySpan<char> word, ref Span<char> result)
3046
{
3147
if (word.IsEmpty)
48+
{
3249
return;
50+
}
3351

34-
int required = result.IsEmpty
35-
? word.Length
36-
: word.Length + 1;
52+
Span<char> destination = result.Slice(resultLength != 0
53+
? resultLength + 1
54+
: resultLength);
3755

38-
if (required >= result.Length)
56+
int written;
57+
while (true)
3958
{
40-
int bufferLength = result.Length * 2;
41-
char[] bufferNew = ArrayPool<char>.Shared.Rent(bufferLength);
42-
43-
result.CopyTo(bufferNew);
59+
written = _lowercase
60+
? word.ToLowerInvariant(destination)
61+
: word.ToUpperInvariant(destination);
4462

45-
if (buffer is not null)
46-
ArrayPool<char>.Shared.Return(buffer);
63+
if (written > 0)
64+
{
65+
break;
66+
}
4767

48-
buffer = bufferNew;
68+
ExpandBuffer(ref result);
4969
}
5070

5171
if (resultLength != 0)
@@ -54,18 +74,7 @@ void WriteWord(ref Span<char> result, ReadOnlySpan<char> word)
5474
resultLength += 1;
5575
}
5676

57-
Span<char> destination = result.Slice(resultLength);
58-
59-
if (_lowercase)
60-
{
61-
word.ToLowerInvariant(destination);
62-
}
63-
else
64-
{
65-
word.ToUpperInvariant(destination);
66-
}
67-
68-
resultLength += word.Length;
77+
resultLength += written;
6978
}
7079

7180
int first = 0;
@@ -81,7 +90,7 @@ void WriteWord(ref Span<char> result, ReadOnlySpan<char> word)
8190
currentCategoryUnicode >= UnicodeCategory.ConnectorPunctuation &&
8291
currentCategoryUnicode <= UnicodeCategory.OtherPunctuation)
8392
{
84-
WriteWord(ref result, chars.Slice(first, index - first));
93+
WriteWord(chars.Slice(first, index - first), ref result);
8594

8695
previousCategory = CharCategory.Boundary;
8796
first = index + 1;
@@ -102,7 +111,7 @@ void WriteWord(ref Span<char> result, ReadOnlySpan<char> word)
102111
if (currentCategory == CharCategory.Lowercase && char.IsUpper(next) ||
103112
next == '_')
104113
{
105-
WriteWord(ref result, chars.Slice(first, index - first + 1));
114+
WriteWord(chars.Slice(first, index - first + 1), ref result);
106115

107116
previousCategory = CharCategory.Boundary;
108117
first = index + 1;
@@ -114,7 +123,7 @@ void WriteWord(ref Span<char> result, ReadOnlySpan<char> word)
114123
currentCategoryUnicode == UnicodeCategory.UppercaseLetter &&
115124
char.IsLower(next))
116125
{
117-
WriteWord(ref result, chars.Slice(first, index - first));
126+
WriteWord(chars.Slice(first, index - first), ref result);
118127

119128
previousCategory = CharCategory.Boundary;
120129
first = index;
@@ -126,14 +135,14 @@ void WriteWord(ref Span<char> result, ReadOnlySpan<char> word)
126135
}
127136
}
128137

129-
WriteWord(ref result, chars.Slice(first));
138+
WriteWord(chars.Slice(first), ref result);
130139

131-
name = result
132-
.Slice(0, resultLength)
133-
.ToString();
140+
name = result.Slice(0, resultLength).ToString();
134141

135142
if (buffer is not null)
136-
ArrayPool<char>.Shared.Return(buffer);
143+
{
144+
ArrayPool<char>.Shared.Return(buffer, clearArray: true);
145+
}
137146

138147
return name;
139148
}

src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@ internal static partial class JsonConstants
5050
public const int SpacesPerIndent = 2;
5151
public const int RemoveFlagsBitMask = 0x7FFFFFFF;
5252

53-
public const int StackallocByteThreshold = 256;
54-
public const int StackallocCharThreshold = StackallocByteThreshold / 2;
55-
5653
// In the worst case, an ASCII character represented as a single utf-8 byte could expand 6x when escaped.
5754
// For example: '+' becomes '\u0043'
5855
// Escaping surrogate pairs (represented by 3 or 4 utf-8 bytes) would expand to 12 bytes (which is still <= 6x).

0 commit comments

Comments
 (0)