Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit ec067c8

Browse files
stephentoubAnipik
authored andcommitted
Avoid unnecessary string allocations in IdnMapping (dotnet/coreclr#17399)
If the output matches the input string, we can just use the input string as the result. Signed-off-by: dotnet-bot-corefx-mirror <dotnet-bot@microsoft.com>
1 parent 275752f commit ec067c8

File tree

3 files changed

+37
-21
lines changed

3 files changed

+37
-21
lines changed

src/Common/src/CoreLib/System/Globalization/IdnMapping.Unix.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ namespace System.Globalization
88
{
99
sealed partial class IdnMapping
1010
{
11-
private unsafe string GetAsciiCore(char* unicode, int count)
11+
private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count)
1212
{
1313
Debug.Assert(!GlobalizationMode.Invariant);
14+
Debug.Assert(unicodeString != null && unicodeString.Length >= count);
1415

1516
uint flags = Flags;
1617
CheckInvalidIdnCharacters(unicode, count, flags, nameof(unicode));
@@ -26,7 +27,7 @@ private unsafe string GetAsciiCore(char* unicode, int count)
2627
actualLength = Interop.Globalization.ToAscii(flags, unicode, count, outputStack, estimatedLength);
2728
if (actualLength > 0 && actualLength <= estimatedLength)
2829
{
29-
return new string(outputStack, 0, actualLength);
30+
return GetStringForOutput(unicodeString, unicode, count, outputStack, actualLength);
3031
}
3132
}
3233
else
@@ -46,13 +47,14 @@ private unsafe string GetAsciiCore(char* unicode, int count)
4647
{
4748
throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(unicode));
4849
}
49-
return new string(pOutputHeap, 0, actualLength);
50+
return GetStringForOutput(unicodeString, unicode, count, pOutputHeap, actualLength);
5051
}
5152
}
5253

53-
private unsafe string GetUnicodeCore(char* ascii, int count)
54+
private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count)
5455
{
5556
Debug.Assert(!GlobalizationMode.Invariant);
57+
Debug.Assert(asciiString != null && asciiString.Length >= count);
5658

5759
uint flags = Flags;
5860
CheckInvalidIdnCharacters(ascii, count, flags, nameof(ascii));
@@ -61,21 +63,22 @@ private unsafe string GetUnicodeCore(char* ascii, int count)
6163
if (count < StackAllocThreshold)
6264
{
6365
char* output = stackalloc char[count];
64-
return GetUnicodeCore(ascii, count, flags, output, count, reattempt: true);
66+
return GetUnicodeCore(asciiString, ascii, count, flags, output, count, reattempt: true);
6567
}
6668
else
6769
{
6870
char[] output = new char[count];
6971
fixed (char* pOutput = &output[0])
7072
{
71-
return GetUnicodeCore(ascii, count, flags, pOutput, count, reattempt: true);
73+
return GetUnicodeCore(asciiString, ascii, count, flags, pOutput, count, reattempt: true);
7274
}
7375
}
7476
}
7577

76-
private unsafe string GetUnicodeCore(char* ascii, int count, uint flags, char* output, int outputLength, bool reattempt)
78+
private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength, bool reattempt)
7779
{
7880
Debug.Assert(!GlobalizationMode.Invariant);
81+
Debug.Assert(asciiString != null && asciiString.Length >= count);
7982

8083
int realLen = Interop.Globalization.ToUnicode(flags, ascii, count, output, outputLength);
8184

@@ -85,14 +88,14 @@ private unsafe string GetUnicodeCore(char* ascii, int count, uint flags, char* o
8588
}
8689
else if (realLen <= outputLength)
8790
{
88-
return new string(output, 0, realLen);
91+
return GetStringForOutput(asciiString, ascii, count, output, realLen);
8992
}
9093
else if (reattempt)
9194
{
9295
char[] newOutput = new char[realLen];
9396
fixed (char* pNewOutput = newOutput)
9497
{
95-
return GetUnicodeCore(ascii, count, flags, pNewOutput, realLen, reattempt: false);
98+
return GetUnicodeCore(asciiString, ascii, count, flags, pNewOutput, realLen, reattempt: false);
9699
}
97100
}
98101

src/Common/src/CoreLib/System/Globalization/IdnMapping.Windows.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ namespace System.Globalization
99
{
1010
public sealed partial class IdnMapping
1111
{
12-
private unsafe string GetAsciiCore(char* unicode, int count)
12+
private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count)
1313
{
1414
Debug.Assert(!GlobalizationMode.Invariant);
15+
Debug.Assert(unicodeString != null && unicodeString.Length >= count);
1516

1617
uint flags = Flags;
1718

@@ -27,34 +28,36 @@ private unsafe string GetAsciiCore(char* unicode, int count)
2728
if (length < StackAllocThreshold)
2829
{
2930
char* output = stackalloc char[length];
30-
return GetAsciiCore(unicode, count, flags, output, length);
31+
return GetAsciiCore(unicodeString, unicode, count, flags, output, length);
3132
}
3233
else
3334
{
3435
char[] output = new char[length];
3536
fixed (char* pOutput = &output[0])
3637
{
37-
return GetAsciiCore(unicode, count, flags, pOutput, length);
38+
return GetAsciiCore(unicodeString, unicode, count, flags, pOutput, length);
3839
}
3940
}
4041
}
4142

42-
private unsafe string GetAsciiCore(char* unicode, int count, uint flags, char* output, int outputLength)
43+
private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count, uint flags, char* output, int outputLength)
4344
{
4445
Debug.Assert(!GlobalizationMode.Invariant);
46+
Debug.Assert(unicodeString != null && unicodeString.Length >= count);
4547

4648
int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, output, outputLength);
4749
if (length == 0)
4850
{
4951
ThrowForZeroLength(unicode: true);
5052
}
5153
Debug.Assert(length == outputLength);
52-
return new string(output, 0, length);
54+
return GetStringForOutput(unicodeString, unicode, count, output, length);
5355
}
5456

55-
private unsafe string GetUnicodeCore(char* ascii, int count)
57+
private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count)
5658
{
5759
Debug.Assert(!GlobalizationMode.Invariant);
60+
Debug.Assert(asciiString != null && asciiString.Length >= count);
5861

5962
uint flags = Flags;
6063

@@ -70,29 +73,30 @@ private unsafe string GetUnicodeCore(char* ascii, int count)
7073
if (length < StackAllocThreshold)
7174
{
7275
char* output = stackalloc char[length];
73-
return GetUnicodeCore(ascii, count, flags, output, length);
76+
return GetUnicodeCore(asciiString, ascii, count, flags, output, length);
7477
}
7578
else
7679
{
7780
char[] output = new char[length];
7881
fixed (char* pOutput = &output[0])
7982
{
80-
return GetUnicodeCore(ascii, count, flags, pOutput, length);
83+
return GetUnicodeCore(asciiString, ascii, count, flags, pOutput, length);
8184
}
8285
}
8386
}
8487

85-
private unsafe string GetUnicodeCore(char* ascii, int count, uint flags, char* output, int outputLength)
88+
private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength)
8689
{
8790
Debug.Assert(!GlobalizationMode.Invariant);
91+
Debug.Assert(asciiString != null && asciiString.Length >= count);
8892

8993
int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, output, outputLength);
9094
if (length == 0)
9195
{
9296
ThrowForZeroLength(unicode: false);
9397
}
9498
Debug.Assert(length == outputLength);
95-
return new string(output, 0, length);
99+
return GetStringForOutput(asciiString, ascii, count, output, length);
96100
}
97101

98102
// -----------------------------

src/Common/src/CoreLib/System/Globalization/IdnMapping.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
// RFC 3492 - Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)
2626

2727
using System.Diagnostics;
28+
using System.Runtime.CompilerServices;
2829
using System.Text;
2930

3031
namespace System.Globalization
@@ -93,7 +94,7 @@ public string GetAscii(string unicode, int index, int count)
9394
{
9495
fixed (char* pUnicode = unicode)
9596
{
96-
return GetAsciiCore(pUnicode + index, count);
97+
return GetAsciiCore(unicode, pUnicode + index, count);
9798
}
9899
}
99100
}
@@ -137,7 +138,7 @@ public string GetUnicode(string ascii, int index, int count)
137138
{
138139
fixed (char* pAscii = ascii)
139140
{
140-
return GetUnicodeCore(pAscii + index, count);
141+
return GetUnicodeCore(ascii, pAscii + index, count);
141142
}
142143
}
143144
}
@@ -156,6 +157,14 @@ public override int GetHashCode()
156157
return (_allowUnassigned ? 100 : 200) + (_useStd3AsciiRules ? 1000 : 2000);
157158
}
158159

160+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
161+
private static unsafe string GetStringForOutput(string originalString, char* input, int inputLength, char* output, int outputLength)
162+
{
163+
return originalString.Length == inputLength && new ReadOnlySpan<char>(input, inputLength).SequenceEqual(new ReadOnlySpan<char>(output, outputLength)) ?
164+
originalString :
165+
new string(output, 0, outputLength);
166+
}
167+
159168
//
160169
// Invariant implementation
161170
//

0 commit comments

Comments
 (0)