From f97b358a6bb5933b451f6dde5582497936b78ca2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 19:54:28 +0000 Subject: [PATCH 1/3] perf: add IndexOfAny fast-path to escapeXml; precompute lowercase key in UniqueNameGenerator - XmlDoc.escapeXml: skip all 3 Replace allocations when the input string contains no XML special characters ('&', '<', '>'). The vast majority of OpenAPI operation summaries and descriptions are plain English text, so the IndexOfAny check amortises to near-zero and avoids 2 intermediate string allocations per call on the hot design-time compilation path. - UniqueNameGenerator.findUniq: precompute prefix.ToLowerInvariant() once per MakeUnique call (in MakeUnique itself) rather than re-computing it on every recursive iteration. This eliminates repeated allocations when two or more schema names collide and the generator appends a numeric suffix. All 389 unit tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/SwaggerProvider.DesignTime/Utils.fs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/SwaggerProvider.DesignTime/Utils.fs b/src/SwaggerProvider.DesignTime/Utils.fs index b82c526d..a49e984b 100644 --- a/src/SwaggerProvider.DesignTime/Utils.fs +++ b/src/SwaggerProvider.DesignTime/Utils.fs @@ -311,8 +311,16 @@ module XmlDoc = open System.Text.Json open System.Text.Json.Nodes + // Fast-path: skip all allocations when the string has no XML special characters. + // In practice the vast majority of OpenAPI descriptions are plain English text, + // so this check pays for itself immediately. + let private xmlSpecialChars = [| '&'; '<'; '>' |] + let private escapeXml(s: string) = - s.Replace("&", "&").Replace("<", "<").Replace(">", ">") + if s.IndexOfAny(xmlSpecialChars) < 0 then + s + else + s.Replace("&", "&").Replace("<", "<").Replace(">", ">") let private formatEnumValue(v: JsonNode) = if isNull v then @@ -385,15 +393,18 @@ type UniqueNameGenerator(?occupiedNames: string seq) = for name in (defaultArg occupiedNames Seq.empty) do hash.Add(name.ToLowerInvariant()) |> ignore - let rec findUniq prefix i = - let newName = sprintf "%s%s" prefix (if i = 0 then "" else i.ToString()) - let key = newName.ToLowerInvariant() + // Compute the lowercase prefix once per MakeUnique call; on each collision + // only the numeric suffix needs to be appended, avoiding repeated ToLowerInvariant + // calls on the full name string. + let rec findUniq prefix prefixLower i = + let newName = if i = 0 then prefix else $"{prefix}{i}" + let key = if i = 0 then prefixLower else $"{prefixLower}{i}" match hash.Contains key with | false -> hash.Add key |> ignore newName - | true -> findUniq prefix (i + 1) + | true -> findUniq prefix prefixLower (i + 1) member _.MakeUnique methodName = - findUniq methodName 0 + findUniq methodName (methodName.ToLowerInvariant()) 0 From 210eed0ab33fb3d4ac9fb3e2908a53d7e53ae7e9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 10 May 2026 19:54:32 +0000 Subject: [PATCH 2/3] ci: trigger checks From 7c5a456e42538a966587c5081e1d823a635f3d90 Mon Sep 17 00:00:00 2001 From: Sergey Tihon Date: Sun, 10 May 2026 22:53:31 +0200 Subject: [PATCH 3/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/SwaggerProvider.DesignTime/Utils.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SwaggerProvider.DesignTime/Utils.fs b/src/SwaggerProvider.DesignTime/Utils.fs index a49e984b..1e871021 100644 --- a/src/SwaggerProvider.DesignTime/Utils.fs +++ b/src/SwaggerProvider.DesignTime/Utils.fs @@ -407,4 +407,5 @@ type UniqueNameGenerator(?occupiedNames: string seq) = | true -> findUniq prefix prefixLower (i + 1) member _.MakeUnique methodName = + let methodName = if isNull methodName then "" else methodName findUniq methodName (methodName.ToLowerInvariant()) 0