Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 8.1.8 - Apr 13 2026

- Code: `NameUtils.niceCamelName` now short-circuits with zero allocations when the result already starts with a lower-case letter; the general case now uses `StringBuilder.Append(string, int, int)` to avoid creating an intermediate `Substring`. `capitalizeFirstLetter` similarly short-circuits when the first letter is already upper-case. Also fixes `Pluralizer` to avoid `Substring(0,1)` in its capitalize path.
- Performance: JSON parser avoids one `Substring` allocation per `\uXXXX` escape by directly indexing into the source string; also uses span-based `Decimal.TryParse`/`Double.TryParse` for number tokens on .NET 8 to avoid a `Substring` allocation per number

## 8.1.7 - Apr 7 2026
Expand Down
22 changes: 18 additions & 4 deletions src/FSharp.Data.Runtime.Utilities/NameUtils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,15 @@ let nicePascalName (s: string) =
let niceCamelName (s: string) =
let name = nicePascalName s

if name.Length > 0 then
name.[0].ToString().ToLowerInvariant() + name.Substring(1)
else
if name.Length = 0 || Char.IsLower(name.[0]) then
// Fast path: already starts with a lower-case letter β€” no allocation needed.
name
else
// Lower-case the first character and append the remainder.
let sb = Text.StringBuilder(name.Length)
sb.Append(Char.ToLowerInvariant(name.[0])) |> ignore
sb.Append(name, 1, name.Length - 1) |> ignore
sb.ToString()

/// Given a function to format names (such as 'niceCamelName' or 'nicePascalName')
/// returns a name generator that never returns duplicate name (by appending an
Expand Down Expand Up @@ -115,8 +120,17 @@ let uniqueGenerator (niceName: string -> string) =
let capitalizeFirstLetter (s: string) =
match s.Length with
| 0 -> ""
| _ when Char.IsUpper(s.[0]) ->
// Fast path: already starts with an upper-case letter β€” no allocation needed.
s
| 1 -> (Char.ToUpperInvariant s.[0]).ToString()
| _ -> (Char.ToUpperInvariant s.[0]).ToString() + s.Substring(1)
| _ ->
// Upper-case the first character and append the remainder via StringBuilder
// to avoid creating two intermediate strings.
let sb = Text.StringBuilder(s.Length)
sb.Append(Char.ToUpperInvariant(s.[0])) |> ignore
sb.Append(s, 1, s.Length - 1) |> ignore
sb.ToString()

/// Trim HTML tags from a given string and replace all of them with spaces
/// Multiple tags are replaced with just a single space. (This is a recursive
Expand Down
2 changes: 1 addition & 1 deletion src/FSharp.Data.Runtime.Utilities/Pluralizer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ let private adjustCase (s: string) (template: string) =
else if allUpper then
s.ToUpperInvariant()
else if firstUpper && not <| Char.IsUpper s.[0] then
s.Substring(0, 1).ToUpperInvariant() + s.Substring(1)
(Char.ToUpperInvariant s.[0]).ToString() + s.Substring(1)
else
s

Expand Down
Loading