Skip to content

Commit 67a113e

Browse files
perf: avoid ToCharArray allocations in TextConversions, HtmlParser, HtmlCssSelectors, HtmlOperations
- TextConversions.RemoveAdorners: replace ToCharArray()+Array.filter with a StringBuilder loop; avoids two intermediate char[] allocations (ToCharArray result + filtered result) on the hot path for every number/date parse attempt - HtmlParser: replace ToCharArray()|>Array.rev string reversal with Array.init using direct index arithmetic; avoids ToCharArray allocation for malformed end-tag detection (e.g. <li></il>) - HtmlCssSelectors.StartsWith: remove ToCharArray(); compare list against string directly via Seq.compareWith (string is IEnumerable<char>) - HtmlCssSelectors.TokenStr: replace ToCharArray()|>Array.toList with List.ofSeq - HtmlCssSelectors.Tokenize: replace ToCharArray()|>Array.toList with List.ofSeq - HtmlOperations AttributeContainsPrefix: remove ToCharArray() before Seq.skipWhile since string is already IEnumerable<char> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 45bc111 commit 67a113e

4 files changed

Lines changed: 18 additions & 14 deletions

File tree

src/FSharp.Data.Html.Core/HtmlCssSelectors.fs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,18 @@ module internal HtmlCssSelectors =
8383
| c -> failwithf "Invalid css selector syntax at: %s" (new String(Array.ofList c))
8484

8585
let (|StartsWith|_|) (s: string) (items: char list) =
86-
let candidates = s.ToCharArray()
87-
88-
if items.Length < candidates.Length then
86+
if items.Length < s.Length then
8987
None
9088
else
91-
let start = items |> Seq.take (candidates.Length) |> Seq.toList
89+
let start = items |> Seq.take s.Length
9290

93-
if (Seq.compareWith (fun a b -> (int a) - (int b)) start candidates) = 0 then
91+
if (Seq.compareWith (fun a b -> (int a) - (int b)) start s) = 0 then
9492
Some(items |> Seq.skip s.Length |> Seq.toList)
9593
else
9694
None
9795

9896
let (|TokenStr|_|) (s: string) x =
99-
let chars = s.ToCharArray() |> Array.toList
97+
let chars = List.ofSeq s
10098

10199
let rec equal x s =
102100
match x, s with
@@ -208,7 +206,7 @@ module internal HtmlCssSelectors =
208206

209207
member public x.Tokenize(pCssSelector: string) =
210208
cssSelector <- pCssSelector
211-
source <- cssSelector.ToCharArray() |> Array.toList
209+
source <- List.ofSeq cssSelector
212210
charCount <- source.Length
213211
tokenize ()
214212

src/FSharp.Data.Html.Core/HtmlOperations.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ module HtmlNode =
367367
let selectedNodes =
368368
filterByAttr level acc name (fun v ->
369369
let chars =
370-
v.ToCharArray()
370+
v
371371
|> Seq.skipWhile (fun c -> c = '\'')
372372
|> Seq.takeWhile Char.IsLetter
373373
|> Seq.toArray

src/FSharp.Data.Html.Core/HtmlParser.fs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,10 @@ module internal HtmlParser =
11131113
parse' docType elements expectedTagEnd parentTagName (TagEnd expectedTagEnd :: tokens)
11141114
| TagEnd name :: rest when
11151115
name <> expectedTagEnd
1116-
&& (name <> (new String(expectedTagEnd.ToCharArray() |> Array.rev)))
1116+
&& (name
1117+
<> (new String(
1118+
Array.init expectedTagEnd.Length (fun i -> expectedTagEnd.[expectedTagEnd.Length - 1 - i])
1119+
)))
11171120
->
11181121
// ignore this token if not the expected end tag (or it's reverse, eg: <li></il>)
11191122
parse' docType elements expectedTagEnd parentTagName rest

src/FSharp.Data.Runtime.Utilities/TextConversions.fs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,14 @@ type TextConversions private () =
104104
// No adorners found, return original string to avoid allocation
105105
value
106106
else
107-
// Adorners found, perform filtering
108-
String(
109-
value.ToCharArray()
110-
|> Array.filter (not << TextConversions.DefaultRemovableAdornerCharacters.Contains)
111-
)
107+
// Adorners found, perform filtering via StringBuilder to avoid ToCharArray allocation
108+
let sb = System.Text.StringBuilder(value.Length)
109+
110+
for c in value do
111+
if not (TextConversions.DefaultRemovableAdornerCharacters.Contains(c)) then
112+
sb.Append(c) |> ignore
113+
114+
sb.ToString()
112115

113116
/// Turns empty or null string value into None, otherwise returns Some
114117
static member AsString str =

0 commit comments

Comments
 (0)