diff --git a/sdk/src/Core/Amazon.Util/AWSSDKUtils.cs b/sdk/src/Core/Amazon.Util/AWSSDKUtils.cs
index 7c2a13fecdc5..e25e74bc7fc6 100644
--- a/sdk/src/Core/Amazon.Util/AWSSDKUtils.cs
+++ b/sdk/src/Core/Amazon.Util/AWSSDKUtils.cs
@@ -195,8 +195,31 @@ private static string DetermineValidPathCharacters()
///
public static string GetExtension(string path)
{
- if (path == null)
+ if (path is null)
return null;
+
+#if NET8_0_OR_GREATER
+ // LastIndexOf and LastIndexOfAny is vectorized on .NET8+ and is
+ // significantly faster for cases where 'path' does not end with a short file
+ // extension, such as GUIDs
+ ReadOnlySpan pathSpan = path.AsSpan();
+ int extensionIndex = pathSpan.LastIndexOf('.');
+ if (extensionIndex == -1)
+ {
+ return string.Empty;
+ }
+
+ int directoryIndex = pathSpan.LastIndexOfAny('/', '\\', ':');
+
+ // extension separator is found and exists before path separator or path separator doesn't exist
+ // AND it's not the last one in the string
+ if (directoryIndex < extensionIndex && extensionIndex < pathSpan.Length - 1)
+ {
+ return pathSpan.Slice(extensionIndex).ToString();
+ }
+
+ return string.Empty;
+#else
int length = path.Length;
int index = length;
@@ -213,15 +236,16 @@ public static string GetExtension(string path)
else if (IsPathSeparator(ch))
break;
}
+
return string.Empty;
- }
- // Checks if the character is one \ / :
- private static bool IsPathSeparator(char ch)
- {
- return (ch == '\\' ||
- ch == '/' ||
- ch == ':');
+ bool IsPathSeparator(char ch)
+ {
+ return (ch == '\\' ||
+ ch == '/' ||
+ ch == ':');
+ }
+#endif
}
/*
diff --git a/sdk/test/NetStandard/UnitTests/Core/AWSSDKUtilsTests.cs b/sdk/test/NetStandard/UnitTests/Core/AWSSDKUtilsTests.cs
index 943692170f4b..8723e0b53785 100644
--- a/sdk/test/NetStandard/UnitTests/Core/AWSSDKUtilsTests.cs
+++ b/sdk/test/NetStandard/UnitTests/Core/AWSSDKUtilsTests.cs
@@ -84,5 +84,22 @@ public void UrlEncodeWithPath(string input, string expected)
Assert.Equal(expected, encoded);
}
+
+ [Theory]
+ [InlineData(null, null)]
+ [InlineData("no-delimiters-at-all", "")]
+ [InlineData("delimiter-end-of-string.", "")]
+ [InlineData("relative-path/no-file-extension", "")]
+ [InlineData("relative-path\\no-file-extension", "")]
+ [InlineData("relative-path:no-file-extension", "")]
+ [InlineData("simple-file.pdf", ".pdf")]
+ [InlineData("relative-path/with-file-extension.pdf", ".pdf")]
+ [InlineData("relative-path.with-dot/with-file-extension.pdf", ".pdf")]
+ public void GetExtension(string input, string expected)
+ {
+ var actual = AWSSDKUtils.GetExtension(input);
+
+ Assert.Equal(expected, actual);
+ }
}
}
diff --git a/sdk/test/UnitTests/Custom/Util/AWSSDKUtilsTests.cs b/sdk/test/UnitTests/Custom/Util/AWSSDKUtilsTests.cs
index d81f11cc2b45..3cd8c6982c21 100644
--- a/sdk/test/UnitTests/Custom/Util/AWSSDKUtilsTests.cs
+++ b/sdk/test/UnitTests/Custom/Util/AWSSDKUtilsTests.cs
@@ -12,13 +12,11 @@
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
+using Amazon.Util;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
-using Amazon.Util;
using System.Reflection;
-using Moq;
-using Amazon.Util.Internal;
using System.Text;
namespace AWSSDK.UnitTests
@@ -178,5 +176,24 @@ public void ToHex(string input, bool lowercase, string expectedResult)
Assert.AreEqual(expectedResult, hexString);
}
+
+ [TestCategory("UnitTest")]
+ [TestCategory("Util")]
+ [DataTestMethod]
+ [DataRow(null, null)]
+ [DataRow("no-delimiters-at-all", "")]
+ [DataRow("delimiter-end-of-string.", "")]
+ [DataRow("relative-path/no-file-extension", "")]
+ [DataRow("relative-path\\no-file-extension", "")]
+ [DataRow("relative-path:no-file-extension", "")]
+ [DataRow("simple-file.pdf", ".pdf")]
+ [DataRow("relative-path/with-file-extension.pdf", ".pdf")]
+ [DataRow("relative-path.with-dot/with-file-extension.pdf", ".pdf")]
+ public void GetExtension(string input, string expected)
+ {
+ var actual = AWSSDKUtils.GetExtension(input);
+
+ Assert.AreEqual(expected, actual);
+ }
}
}