diff --git a/src/Argon/NamingStrategy/CamelCaseNamingStrategy.cs b/src/Argon/NamingStrategy/CamelCaseNamingStrategy.cs index c50385c41..686bb9e49 100644 --- a/src/Argon/NamingStrategy/CamelCaseNamingStrategy.cs +++ b/src/Argon/NamingStrategy/CamelCaseNamingStrategy.cs @@ -46,38 +46,38 @@ internal static string ToCamelCase(string s) return s; } - var chars = s.ToCharArray(); - - for (var i = 0; i < chars.Length; i++) + return string.Create(s.Length, s, static (span, source) => { - var ch = chars[i]; - if (i == 1 && !char.IsUpper(ch)) + source.AsSpan().CopyTo(span); + for (var i = 0; i < span.Length; i++) { - break; - } - - var hasNext = i + 1 < chars.Length; - if (i > 0 && hasNext && !char.IsUpper(chars[i + 1])) - { - // if the next character is a space, which is not considered uppercase - // (otherwise we wouldn't be here...) - // we want to ensure that the following: - // 'FOO bar' is rewritten as 'foo bar', and not as 'foO bar' - // The code was written in such a way that the first word in uppercase - // ends when if finds an uppercase letter followed by a lowercase letter. - // now a ' ' (space, (char)32) is considered not upper - // but in that case we still want our current character to become lowercase - if (char.IsSeparator(chars[i + 1])) + var ch = span[i]; + if (i == 1 && !char.IsUpper(ch)) { - chars[i] = char.ToLower(ch, InvariantCulture); + break; } - break; - } + var hasNext = i + 1 < span.Length; + if (i > 0 && hasNext && !char.IsUpper(span[i + 1])) + { + // if the next character is a space, which is not considered uppercase + // (otherwise we wouldn't be here...) + // we want to ensure that the following: + // 'FOO bar' is rewritten as 'foo bar', and not as 'foO bar' + // The code was written in such a way that the first word in uppercase + // ends when if finds an uppercase letter followed by a lowercase letter. + // now a ' ' (space, (char)32) is considered not upper + // but in that case we still want our current character to become lowercase + if (char.IsSeparator(span[i + 1])) + { + span[i] = char.ToLower(ch, InvariantCulture); + } - chars[i] = char.ToLower(ch, InvariantCulture); - } + break; + } - return new(chars); + span[i] = char.ToLower(ch, InvariantCulture); + } + }); } } \ No newline at end of file diff --git a/src/ArgonTests/Benchmarks/CamelCaseBenchmarks.cs b/src/ArgonTests/Benchmarks/CamelCaseBenchmarks.cs new file mode 100644 index 000000000..0db915195 --- /dev/null +++ b/src/ArgonTests/Benchmarks/CamelCaseBenchmarks.cs @@ -0,0 +1,37 @@ +// Copyright (c) 2007 James Newton-King. All rights reserved. +// Use of this source code is governed by The MIT License, +// as found in the license.md file. + +using BenchmarkDotNet.Attributes; + +[MemoryDiagnoser] +public class CamelCaseBenchmarks +{ + static readonly string[] names = + [ + "Id", + "Name", + "FirstName", + "LastName", + "EmailAddress", + "PhoneNumber", + "DateOfBirth", + "IsActive", + "CreatedAt", + "UpdatedAt", + "URL", + "HTTPStatusCode", + "XMLDocument", + "SomeVeryLongPropertyNameThatExercisesTheLoop", + "ABC" + ]; + + [Benchmark] + public void Run() + { + for (var i = 0; i < names.Length; i++) + { + CamelCaseNamingStrategy.ToCamelCase(names[i]); + } + } +} diff --git a/src/Benchmark.Tests/Program.cs b/src/Benchmark.Tests/Program.cs index 02f1ebe11..5d4cfe6e1 100644 --- a/src/Benchmark.Tests/Program.cs +++ b/src/Benchmark.Tests/Program.cs @@ -11,7 +11,7 @@ public static void Main(string[] args) var attribute = (AssemblyFileVersionAttribute)typeof(JsonConvert).Assembly.GetCustomAttribute(typeof(AssemblyFileVersionAttribute))!; Console.WriteLine($"Json.NET Version: {attribute.Version}"); - var switcher = new BenchmarkSwitcher([typeof(WriteEscapedJavaScriptString), typeof(SerializeJTokenList), typeof(ReadQuotedNumbers), typeof(WriteBase64Benchmark)]); + var switcher = new BenchmarkSwitcher([typeof(WriteEscapedJavaScriptString), typeof(SerializeJTokenList), typeof(CamelCaseBenchmarks), typeof(ReadQuotedNumbers), typeof(WriteBase64Benchmark)]); if (args.Length == 0) { switcher.Run(["*"]);