diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index 929ee6132d851..da4412098aa6f 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -4220,4 +4220,7 @@
An unexpected state object was encountered. This is usually a sign of a bug in async method custom infrastructure, such as a custom awaiter or IValueTaskSource implementation.
+
+ The resulting string is too long
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs
index 7a8dac5644f83..77251e080cb55 100644
--- a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs
@@ -38,14 +38,11 @@ internal static class SearchValuesStorage
internal const int StackallocIntBufferSizeLimit = 128;
internal const int StackallocCharBufferSizeLimit = 256;
- private static void FillStringChecked(string dest, int destPos, string src)
+ private static void CopyStringContent(string dest, int destPos, string src)
{
Debug.Assert(dest != null);
Debug.Assert(src != null);
- if (src.Length > dest.Length - destPos)
- {
- throw new IndexOutOfRangeException();
- }
+ Debug.Assert(src.Length <= dest.Length - destPos);
Buffer.Memmove(
destination: ref Unsafe.Add(ref dest._firstChar, destPos),
@@ -97,7 +94,7 @@ public static string Concat(params object?[] args)
if (totalLength < 0) // Check for a positive overflow
{
- throw new OutOfMemoryException();
+ ThrowHelper.ThrowOutOfMemoryException_StringTooLong();
}
}
@@ -117,7 +114,7 @@ public static string Concat(params object?[] args)
Debug.Assert(s != null);
Debug.Assert(position <= totalLength - s.Length, "We didn't allocate enough space for the result string!");
- FillStringChecked(result, position, s);
+ CopyStringContent(result, position, s);
position += s.Length;
}
@@ -255,10 +252,18 @@ public static string Concat(string? str0, string? str1)
int str0Length = str0.Length;
- string result = FastAllocateString(str0Length + str1.Length);
+ int totalLength = str0Length + str1.Length;
+
+ // Can't overflow to a positive number so just check < 0
+ if (totalLength < 0)
+ {
+ ThrowHelper.ThrowOutOfMemoryException_StringTooLong();
+ }
+
+ string result = FastAllocateString(totalLength);
- FillStringChecked(result, 0, str0);
- FillStringChecked(result, str0Length, str1);
+ CopyStringContent(result, 0, str0);
+ CopyStringContent(result, str0Length, str1);
return result;
}
@@ -280,12 +285,18 @@ public static string Concat(string? str0, string? str1, string? str2)
return Concat(str0, str1);
}
- int totalLength = str0.Length + str1.Length + str2.Length;
+ // It can overflow to a positive number so we accumulate the total length as a long.
+ long totalLength = (long)str0.Length + (long)str1.Length + (long)str2.Length;
- string result = FastAllocateString(totalLength);
- FillStringChecked(result, 0, str0);
- FillStringChecked(result, str0.Length, str1);
- FillStringChecked(result, str0.Length + str1.Length, str2);
+ if (totalLength > int.MaxValue)
+ {
+ ThrowHelper.ThrowOutOfMemoryException_StringTooLong();
+ }
+
+ string result = FastAllocateString((int)totalLength);
+ CopyStringContent(result, 0, str0);
+ CopyStringContent(result, str0.Length, str1);
+ CopyStringContent(result, str0.Length + str1.Length, str2);
return result;
}
@@ -312,13 +323,19 @@ public static string Concat(string? str0, string? str1, string? str2, string? st
return Concat(str0, str1, str2);
}
- int totalLength = str0.Length + str1.Length + str2.Length + str3.Length;
+ // It can overflow to a positive number so we accumulate the total length as a long.
+ long totalLength = (long)str0.Length + (long)str1.Length + (long)str2.Length + (long)str3.Length;
- string result = FastAllocateString(totalLength);
- FillStringChecked(result, 0, str0);
- FillStringChecked(result, str0.Length, str1);
- FillStringChecked(result, str0.Length + str1.Length, str2);
- FillStringChecked(result, str0.Length + str1.Length + str2.Length, str3);
+ if (totalLength > int.MaxValue)
+ {
+ ThrowHelper.ThrowOutOfMemoryException_StringTooLong();
+ }
+
+ string result = FastAllocateString((int)totalLength);
+ CopyStringContent(result, 0, str0);
+ CopyStringContent(result, str0.Length, str1);
+ CopyStringContent(result, str0.Length + str1.Length, str2);
+ CopyStringContent(result, str0.Length + str1.Length + str2.Length, str3);
return result;
}
@@ -447,7 +464,7 @@ public static string Concat(params string?[] values)
// If it's too long, fail, or if it's empty, return an empty string.
if (totalLengthLong > int.MaxValue)
{
- throw new OutOfMemoryException();
+ ThrowHelper.ThrowOutOfMemoryException_StringTooLong();
}
int totalLength = (int)totalLengthLong;
if (totalLength == 0)
@@ -470,7 +487,7 @@ public static string Concat(params string?[] values)
break;
}
- FillStringChecked(result, copiedLength, value);
+ CopyStringContent(result, copiedLength, value);
copiedLength += valueLen;
}
}
@@ -931,7 +948,7 @@ private static string JoinCore(ReadOnlySpan separator, ReadOnlySpan int.MaxValue)
{
- ThrowHelper.ThrowOutOfMemoryException();
+ ThrowHelper.ThrowOutOfMemoryException_StringTooLong();
}
int totalLength = (int)totalSeparatorsLength;
@@ -943,7 +960,7 @@ private static string JoinCore(ReadOnlySpan separator, ReadOnlySpan separator, ReadOnlySpan int.MaxValue)
- throw new OutOfMemoryException();
+ ThrowHelper.ThrowOutOfMemoryException_StringTooLong();
string dst = FastAllocateString((int)dstLength);
Span dstSpan = new Span(ref dst._firstChar, dst.Length);
diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
index d380e9d7533d7..95c49059600b6 100644
--- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
@@ -443,6 +443,12 @@ internal static void ThrowOutOfMemoryException()
throw new OutOfMemoryException();
}
+ [DoesNotReturn]
+ internal static void ThrowOutOfMemoryException_StringTooLong()
+ {
+ throw new OutOfMemoryException(SR.OutOfMemory_StringTooLong);
+ }
+
[DoesNotReturn]
internal static void ThrowArgumentException_Argument_IncompatibleArrayType()
{