From f1910c40d45f76ca24af23c9b51c8949e6d08416 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 13 May 2026 09:41:36 +1000 Subject: [PATCH] Add StringBuilder MoveChunks --- apiCount.include.md | 46 +++--- api_list.include.md | 1 + assemblySize.include.md | 84 +++++------ readme.md | 131 +++++++++--------- src/Consume/Consume.cs | 1 + src/Polyfill/StringBuilderPolyfill.cs | 63 +++++++++ src/Split/net10.0/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net11.0/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net461/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net462/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net47/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net471/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net472/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net48/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net481/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net5.0/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net6.0/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net7.0/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net8.0/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/net9.0/StringBuilderPolyfill.cs | 54 ++++++++ .../netcoreapp2.0/StringBuilderPolyfill.cs | 54 ++++++++ .../netcoreapp2.1/StringBuilderPolyfill.cs | 54 ++++++++ .../netcoreapp2.2/StringBuilderPolyfill.cs | 54 ++++++++ .../netcoreapp3.0/StringBuilderPolyfill.cs | 54 ++++++++ .../netcoreapp3.1/StringBuilderPolyfill.cs | 54 ++++++++ .../netstandard2.0/StringBuilderPolyfill.cs | 54 ++++++++ .../netstandard2.1/StringBuilderPolyfill.cs | 54 ++++++++ src/Split/uap10.0/StringBuilderPolyfill.cs | 54 ++++++++ src/Tests/PolyfillTests_StringBuilder.cs | 23 +++ 29 files changed, 1407 insertions(+), 130 deletions(-) create mode 100644 src/Polyfill/StringBuilderPolyfill.cs create mode 100644 src/Split/net10.0/StringBuilderPolyfill.cs create mode 100644 src/Split/net11.0/StringBuilderPolyfill.cs create mode 100644 src/Split/net461/StringBuilderPolyfill.cs create mode 100644 src/Split/net462/StringBuilderPolyfill.cs create mode 100644 src/Split/net47/StringBuilderPolyfill.cs create mode 100644 src/Split/net471/StringBuilderPolyfill.cs create mode 100644 src/Split/net472/StringBuilderPolyfill.cs create mode 100644 src/Split/net48/StringBuilderPolyfill.cs create mode 100644 src/Split/net481/StringBuilderPolyfill.cs create mode 100644 src/Split/net5.0/StringBuilderPolyfill.cs create mode 100644 src/Split/net6.0/StringBuilderPolyfill.cs create mode 100644 src/Split/net7.0/StringBuilderPolyfill.cs create mode 100644 src/Split/net8.0/StringBuilderPolyfill.cs create mode 100644 src/Split/net9.0/StringBuilderPolyfill.cs create mode 100644 src/Split/netcoreapp2.0/StringBuilderPolyfill.cs create mode 100644 src/Split/netcoreapp2.1/StringBuilderPolyfill.cs create mode 100644 src/Split/netcoreapp2.2/StringBuilderPolyfill.cs create mode 100644 src/Split/netcoreapp3.0/StringBuilderPolyfill.cs create mode 100644 src/Split/netcoreapp3.1/StringBuilderPolyfill.cs create mode 100644 src/Split/netstandard2.0/StringBuilderPolyfill.cs create mode 100644 src/Split/netstandard2.1/StringBuilderPolyfill.cs create mode 100644 src/Split/uap10.0/StringBuilderPolyfill.cs diff --git a/apiCount.include.md b/apiCount.include.md index 99bea8de..2eef5f3d 100644 --- a/apiCount.include.md +++ b/apiCount.include.md @@ -1,28 +1,28 @@ -**API count: 937** +**API count: 938** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 907 | -| `net462` | 907 | -| `net47` | 906 | -| `net471` | 905 | -| `net472` | 901 | -| `net48` | 901 | -| `net481` | 901 | -| `netstandard2.0` | 903 | -| `netstandard2.1` | 734 | -| `netcoreapp2.0` | 827 | -| `netcoreapp2.1` | 746 | -| `netcoreapp2.2` | 746 | -| `netcoreapp3.0` | 692 | -| `netcoreapp3.1` | 691 | -| `net5.0` | 563 | -| `net6.0` | 468 | -| `net7.0` | 315 | -| `net8.0` | 197 | -| `net9.0` | 128 | -| `net10.0` | 76 | -| `net11.0` | 57 | -| `uap10.0` | 893 | +| `net461` | 908 | +| `net462` | 908 | +| `net47` | 907 | +| `net471` | 906 | +| `net472` | 902 | +| `net48` | 902 | +| `net481` | 902 | +| `netstandard2.0` | 904 | +| `netstandard2.1` | 735 | +| `netcoreapp2.0` | 828 | +| `netcoreapp2.1` | 747 | +| `netcoreapp2.2` | 747 | +| `netcoreapp3.0` | 693 | +| `netcoreapp3.1` | 692 | +| `net5.0` | 564 | +| `net6.0` | 469 | +| `net7.0` | 316 | +| `net8.0` | 198 | +| `net9.0` | 129 | +| `net10.0` | 77 | +| `net11.0` | 58 | +| `uap10.0` | 894 | diff --git a/api_list.include.md b/api_list.include.md index cac42e88..0b35a264 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -1080,6 +1080,7 @@ * `StringBuilder Insert(int, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.insert?view=net-11.0#system-text-stringbuilder-insert(system-int32-system-readonlyspan((system-char)))) * `StringBuilder Replace(ReadOnlySpan, ReadOnlySpan, int, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.replace?view=net-11.0#system-text-stringbuilder-replace(system-char-system-char-system-int32-system-int32)) * `StringBuilder Replace(ReadOnlySpan, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.replace?view=net-11.0#system-text-stringbuilder-replace(system-readonlyspan((system-char))-system-readonlyspan((system-char)))) + * `StringBuilder MoveChunks(StringBuilder)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.movechunks?view=net-11.0) #### Task diff --git a/assemblySize.include.md b/assemblySize.include.md index 46c2f81e..68eff2f0 100644 --- a/assemblySize.include.md +++ b/assemblySize.include.md @@ -2,51 +2,51 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 306.5KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netstandard2.1 | 8.5KB | 259.5KB | +251.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net461 | 8.5KB | 305.0KB | +296.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net462 | 7.0KB | 308.5KB | +301.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net47 | 7.0KB | 308.5KB | +301.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net471 | 8.5KB | 307.5KB | +299.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net472 | 8.5KB | 306.0KB | +297.5KB | +9.0KB | +7.0KB | +9.5KB | +14.0KB | -| net48 | 8.5KB | 306.0KB | +297.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net481 | 8.5KB | 306.5KB | +298.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 282.5KB | +273.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp2.1 | 9.0KB | 262.0KB | +253.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 262.0KB | +253.0KB | +9.0KB | +7.0KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 253.5KB | +244.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 251.5KB | +242.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net5.0 | 9.5KB | 214.5KB | +205.0KB | +9.5KB | +7.0KB | +9.5KB | +14.5KB | -| net6.0 | 10.0KB | 153.0KB | +143.0KB | +10.0KB | +7.0KB | +512bytes | +4.0KB | -| net7.0 | 10.0KB | 118.0KB | +108.0KB | +9.0KB | +5.5KB | +512bytes | +3.5KB | -| net8.0 | 9.5KB | 89.5KB | +80.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB | -| net9.0 | 9.5KB | 47.5KB | +38.0KB | +9.0KB | | +512bytes | +3.5KB | -| net10.0 | 10.0KB | 24.0KB | +14.0KB | +9.0KB | | +512bytes | +3.5KB | -| net11.0 | 10.0KB | 18.5KB | +8.5KB | +9.0KB | | +512bytes | +3.5KB | +| netstandard2.0 | 8.0KB | 307.5KB | +299.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netstandard2.1 | 8.5KB | 260.5KB | +252.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 306.0KB | +297.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net462 | 7.0KB | 309.5KB | +302.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net47 | 7.0KB | 309.5KB | +302.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net471 | 8.5KB | 308.5KB | +300.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net472 | 8.5KB | 307.0KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net48 | 8.5KB | 307.0KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net481 | 8.5KB | 307.0KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.0 | 9.0KB | 283.5KB | +274.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.1 | 9.0KB | 263.0KB | +254.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 263.0KB | +254.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 254.5KB | +245.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 252.5KB | +243.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net5.0 | 9.5KB | 215.5KB | +206.0KB | +9.5KB | +7.0KB | +9.5KB | +14.5KB | +| net6.0 | 10.0KB | 154.5KB | +144.5KB | +9.5KB | +7.0KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 119.5KB | +109.5KB | +9.0KB | +5.0KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 91.0KB | +81.5KB | +8.5KB | | +512bytes | +3.5KB | +| net9.0 | 9.5KB | 49.0KB | +39.5KB | +8.5KB | | +512bytes | +3.5KB | +| net10.0 | 10.0KB | 25.5KB | +15.5KB | +8.5KB | | +512bytes | +3.0KB | +| net11.0 | 10.0KB | 20.0KB | +10.0KB | +9.0KB | | +512bytes | +3.5KB | ### Assembly Sizes with EmbedUntrackedSources | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 446.3KB | +438.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netstandard2.1 | 8.5KB | 374.1KB | +365.6KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net461 | 8.5KB | 445.8KB | +437.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net462 | 7.0KB | 449.3KB | +442.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net47 | 7.0KB | 449.1KB | +442.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net471 | 8.5KB | 447.7KB | +439.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net472 | 8.5KB | 445.1KB | +436.6KB | +16.7KB | +8.7KB | +14.4KB | +19.4KB | -| net48 | 8.5KB | 445.1KB | +436.6KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net481 | 8.5KB | 445.6KB | +437.1KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 412.4KB | +403.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp2.1 | 9.0KB | 380.8KB | +371.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 380.8KB | +371.8KB | +16.7KB | +8.7KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 363.0KB | +353.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 360.9KB | +351.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net5.0 | 9.5KB | 305.8KB | +296.3KB | +17.2KB | +8.7KB | +14.4KB | +19.9KB | -| net6.0 | 10.0KB | 224.7KB | +214.7KB | +17.7KB | +8.7KB | +1.1KB | +4.7KB | -| net7.0 | 10.0KB | 170.9KB | +160.9KB | +16.6KB | +6.9KB | +1.1KB | +4.2KB | -| net8.0 | 9.5KB | 127.6KB | +118.1KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | -| net9.0 | 9.5KB | 68.6KB | +59.1KB | +16.5KB | | +1.1KB | +4.2KB | -| net10.0 | 10.0KB | 36.5KB | +26.5KB | +16.5KB | | +1.1KB | +4.2KB | -| net11.0 | 10.0KB | 27.4KB | +17.4KB | +16.5KB | | +1.1KB | +4.2KB | +| netstandard2.0 | 8.0KB | 448.1KB | +440.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netstandard2.1 | 8.5KB | 375.9KB | +367.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 447.6KB | +439.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net462 | 7.0KB | 451.1KB | +444.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net47 | 7.0KB | 450.9KB | +443.9KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net471 | 8.5KB | 449.5KB | +441.0KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net472 | 8.5KB | 446.9KB | +438.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net48 | 8.5KB | 446.9KB | +438.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net481 | 8.5KB | 446.9KB | +438.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.0 | 9.0KB | 414.2KB | +405.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.1 | 9.0KB | 382.6KB | +373.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 382.6KB | +373.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 364.8KB | +355.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 362.7KB | +353.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net5.0 | 9.5KB | 307.6KB | +298.1KB | +17.2KB | +8.7KB | +14.4KB | +19.9KB | +| net6.0 | 10.0KB | 227.0KB | +217.0KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 173.2KB | +163.2KB | +16.6KB | +6.4KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 129.8KB | +120.3KB | +16.0KB | +299bytes | +1.1KB | +4.2KB | +| net9.0 | 9.5KB | 70.9KB | +61.4KB | +16.0KB | | +1.1KB | +4.2KB | +| net10.0 | 10.0KB | 38.8KB | +28.8KB | +16.0KB | | +1.1KB | +3.7KB | +| net11.0 | 10.0KB | 29.7KB | +19.7KB | +16.5KB | | +1.1KB | +4.2KB | diff --git a/readme.md b/readme.md index a107e5f6..4f0dd9e6 100644 --- a/readme.md +++ b/readme.md @@ -13,34 +13,34 @@ The package targets `netstandard2.0` and is designed to support the following ru * `uap10` -**API count: 937** +**API count: 938** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 907 | -| `net462` | 907 | -| `net47` | 906 | -| `net471` | 905 | -| `net472` | 901 | -| `net48` | 901 | -| `net481` | 901 | -| `netstandard2.0` | 903 | -| `netstandard2.1` | 734 | -| `netcoreapp2.0` | 827 | -| `netcoreapp2.1` | 746 | -| `netcoreapp2.2` | 746 | -| `netcoreapp3.0` | 692 | -| `netcoreapp3.1` | 691 | -| `net5.0` | 563 | -| `net6.0` | 468 | -| `net7.0` | 315 | -| `net8.0` | 197 | -| `net9.0` | 128 | -| `net10.0` | 76 | -| `net11.0` | 57 | -| `uap10.0` | 893 | +| `net461` | 908 | +| `net462` | 908 | +| `net47` | 907 | +| `net471` | 906 | +| `net472` | 902 | +| `net48` | 902 | +| `net481` | 902 | +| `netstandard2.0` | 904 | +| `netstandard2.1` | 735 | +| `netcoreapp2.0` | 828 | +| `netcoreapp2.1` | 747 | +| `netcoreapp2.2` | 747 | +| `netcoreapp3.0` | 693 | +| `netcoreapp3.1` | 692 | +| `net5.0` | 564 | +| `net6.0` | 469 | +| `net7.0` | 316 | +| `net8.0` | 198 | +| `net9.0` | 129 | +| `net10.0` | 77 | +| `net11.0` | 58 | +| `uap10.0` | 894 | @@ -96,54 +96,54 @@ This project uses features from the current stable SDK and C# language. As such | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 306.5KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netstandard2.1 | 8.5KB | 259.5KB | +251.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net461 | 8.5KB | 305.0KB | +296.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net462 | 7.0KB | 308.5KB | +301.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net47 | 7.0KB | 308.5KB | +301.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net471 | 8.5KB | 307.5KB | +299.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net472 | 8.5KB | 306.0KB | +297.5KB | +9.0KB | +7.0KB | +9.5KB | +14.0KB | -| net48 | 8.5KB | 306.0KB | +297.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net481 | 8.5KB | 306.5KB | +298.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 282.5KB | +273.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp2.1 | 9.0KB | 262.0KB | +253.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 262.0KB | +253.0KB | +9.0KB | +7.0KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 253.5KB | +244.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 251.5KB | +242.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net5.0 | 9.5KB | 214.5KB | +205.0KB | +9.5KB | +7.0KB | +9.5KB | +14.5KB | -| net6.0 | 10.0KB | 153.0KB | +143.0KB | +10.0KB | +7.0KB | +512bytes | +4.0KB | -| net7.0 | 10.0KB | 118.0KB | +108.0KB | +9.0KB | +5.5KB | +512bytes | +3.5KB | -| net8.0 | 9.5KB | 89.5KB | +80.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB | -| net9.0 | 9.5KB | 47.5KB | +38.0KB | +9.0KB | | +512bytes | +3.5KB | -| net10.0 | 10.0KB | 24.0KB | +14.0KB | +9.0KB | | +512bytes | +3.5KB | -| net11.0 | 10.0KB | 18.5KB | +8.5KB | +9.0KB | | +512bytes | +3.5KB | +| netstandard2.0 | 8.0KB | 307.5KB | +299.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netstandard2.1 | 8.5KB | 260.5KB | +252.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 306.0KB | +297.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net462 | 7.0KB | 309.5KB | +302.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net47 | 7.0KB | 309.5KB | +302.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net471 | 8.5KB | 308.5KB | +300.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net472 | 8.5KB | 307.0KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net48 | 8.5KB | 307.0KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net481 | 8.5KB | 307.0KB | +298.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.0 | 9.0KB | 283.5KB | +274.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.1 | 9.0KB | 263.0KB | +254.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 263.0KB | +254.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 254.5KB | +245.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 252.5KB | +243.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net5.0 | 9.5KB | 215.5KB | +206.0KB | +9.5KB | +7.0KB | +9.5KB | +14.5KB | +| net6.0 | 10.0KB | 154.5KB | +144.5KB | +9.5KB | +7.0KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 119.5KB | +109.5KB | +9.0KB | +5.0KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 91.0KB | +81.5KB | +8.5KB | | +512bytes | +3.5KB | +| net9.0 | 9.5KB | 49.0KB | +39.5KB | +8.5KB | | +512bytes | +3.5KB | +| net10.0 | 10.0KB | 25.5KB | +15.5KB | +8.5KB | | +512bytes | +3.0KB | +| net11.0 | 10.0KB | 20.0KB | +10.0KB | +9.0KB | | +512bytes | +3.5KB | ### Assembly Sizes with EmbedUntrackedSources | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 446.3KB | +438.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netstandard2.1 | 8.5KB | 374.1KB | +365.6KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net461 | 8.5KB | 445.8KB | +437.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net462 | 7.0KB | 449.3KB | +442.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net47 | 7.0KB | 449.1KB | +442.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net471 | 8.5KB | 447.7KB | +439.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net472 | 8.5KB | 445.1KB | +436.6KB | +16.7KB | +8.7KB | +14.4KB | +19.4KB | -| net48 | 8.5KB | 445.1KB | +436.6KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net481 | 8.5KB | 445.6KB | +437.1KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 412.4KB | +403.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp2.1 | 9.0KB | 380.8KB | +371.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 380.8KB | +371.8KB | +16.7KB | +8.7KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 363.0KB | +353.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 360.9KB | +351.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net5.0 | 9.5KB | 305.8KB | +296.3KB | +17.2KB | +8.7KB | +14.4KB | +19.9KB | -| net6.0 | 10.0KB | 224.7KB | +214.7KB | +17.7KB | +8.7KB | +1.1KB | +4.7KB | -| net7.0 | 10.0KB | 170.9KB | +160.9KB | +16.6KB | +6.9KB | +1.1KB | +4.2KB | -| net8.0 | 9.5KB | 127.6KB | +118.1KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | -| net9.0 | 9.5KB | 68.6KB | +59.1KB | +16.5KB | | +1.1KB | +4.2KB | -| net10.0 | 10.0KB | 36.5KB | +26.5KB | +16.5KB | | +1.1KB | +4.2KB | -| net11.0 | 10.0KB | 27.4KB | +17.4KB | +16.5KB | | +1.1KB | +4.2KB | +| netstandard2.0 | 8.0KB | 448.1KB | +440.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netstandard2.1 | 8.5KB | 375.9KB | +367.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 447.6KB | +439.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net462 | 7.0KB | 451.1KB | +444.1KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net47 | 7.0KB | 450.9KB | +443.9KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net471 | 8.5KB | 449.5KB | +441.0KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net472 | 8.5KB | 446.9KB | +438.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net48 | 8.5KB | 446.9KB | +438.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net481 | 8.5KB | 446.9KB | +438.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.0 | 9.0KB | 414.2KB | +405.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.1 | 9.0KB | 382.6KB | +373.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 382.6KB | +373.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 364.8KB | +355.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 362.7KB | +353.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net5.0 | 9.5KB | 307.6KB | +298.1KB | +17.2KB | +8.7KB | +14.4KB | +19.9KB | +| net6.0 | 10.0KB | 227.0KB | +217.0KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 173.2KB | +163.2KB | +16.6KB | +6.4KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 129.8KB | +120.3KB | +16.0KB | +299bytes | +1.1KB | +4.2KB | +| net9.0 | 9.5KB | 70.9KB | +61.4KB | +16.0KB | | +1.1KB | +4.2KB | +| net10.0 | 10.0KB | 38.8KB | +28.8KB | +16.0KB | | +1.1KB | +3.7KB | +| net11.0 | 10.0KB | 29.7KB | +19.7KB | +16.5KB | | +1.1KB | +4.2KB | @@ -1611,6 +1611,7 @@ The class `Polyfill` includes the following extension methods: * `StringBuilder Insert(int, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.insert?view=net-11.0#system-text-stringbuilder-insert(system-int32-system-readonlyspan((system-char)))) * `StringBuilder Replace(ReadOnlySpan, ReadOnlySpan, int, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.replace?view=net-11.0#system-text-stringbuilder-replace(system-char-system-char-system-int32-system-int32)) * `StringBuilder Replace(ReadOnlySpan, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.replace?view=net-11.0#system-text-stringbuilder-replace(system-readonlyspan((system-char))-system-readonlyspan((system-char)))) + * `StringBuilder MoveChunks(StringBuilder)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.movechunks?view=net-11.0) #### Task diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index 353fb8c2..240dae83 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -1413,6 +1413,7 @@ void DefaultInterpolatedStringHandler_Methods() void StringBuilder_Methods() { var builder = new StringBuilder("value"); + var moved = StringBuilder.MoveChunks(builder); #if FeatureMemory builder.Append("suffix".AsSpan()); var targetSpan = new Span(new char[1]); diff --git a/src/Polyfill/StringBuilderPolyfill.cs b/src/Polyfill/StringBuilderPolyfill.cs new file mode 100644 index 00000000..860747ce --- /dev/null +++ b/src/Polyfill/StringBuilderPolyfill.cs @@ -0,0 +1,63 @@ +#if !NET12_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Reflection; +using System.Text; + +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.movechunks?view=net-11.0 + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + + return destination; + } + } + + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} + +#endif diff --git a/src/Split/net10.0/StringBuilderPolyfill.cs b/src/Split/net10.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net10.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net11.0/StringBuilderPolyfill.cs b/src/Split/net11.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net11.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net461/StringBuilderPolyfill.cs b/src/Split/net461/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net461/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net462/StringBuilderPolyfill.cs b/src/Split/net462/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net462/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net47/StringBuilderPolyfill.cs b/src/Split/net47/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net47/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net471/StringBuilderPolyfill.cs b/src/Split/net471/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net471/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net472/StringBuilderPolyfill.cs b/src/Split/net472/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net472/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net48/StringBuilderPolyfill.cs b/src/Split/net48/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net48/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net481/StringBuilderPolyfill.cs b/src/Split/net481/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net481/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net5.0/StringBuilderPolyfill.cs b/src/Split/net5.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net5.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net6.0/StringBuilderPolyfill.cs b/src/Split/net6.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net6.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net7.0/StringBuilderPolyfill.cs b/src/Split/net7.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net7.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net8.0/StringBuilderPolyfill.cs b/src/Split/net8.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net8.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/net9.0/StringBuilderPolyfill.cs b/src/Split/net9.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/net9.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp2.0/StringBuilderPolyfill.cs b/src/Split/netcoreapp2.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp2.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp2.1/StringBuilderPolyfill.cs b/src/Split/netcoreapp2.1/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp2.1/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp2.2/StringBuilderPolyfill.cs b/src/Split/netcoreapp2.2/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp2.2/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp3.0/StringBuilderPolyfill.cs b/src/Split/netcoreapp3.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp3.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netcoreapp3.1/StringBuilderPolyfill.cs b/src/Split/netcoreapp3.1/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netcoreapp3.1/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netstandard2.0/StringBuilderPolyfill.cs b/src/Split/netstandard2.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netstandard2.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/netstandard2.1/StringBuilderPolyfill.cs b/src/Split/netstandard2.1/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/netstandard2.1/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Split/uap10.0/StringBuilderPolyfill.cs b/src/Split/uap10.0/StringBuilderPolyfill.cs new file mode 100644 index 00000000..cddb85db --- /dev/null +++ b/src/Split/uap10.0/StringBuilderPolyfill.cs @@ -0,0 +1,54 @@ +// +#pragma warning disable +#if !NET12_0_OR_GREATER +namespace Polyfills; +using System; +using System.Reflection; +using System.Text; +static partial class Polyfill +{ + extension(StringBuilder) + { + /// + /// Creates a new instance initialized to the same state as + /// , and resets to an empty, usable state + /// with no allocated buffers. Ownership of the chunks is transferred in O(1); the underlying + /// character data is not copied. 's + /// is preserved. + /// + public static StringBuilder MoveChunks(StringBuilder source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + var charsField = MoveChunksFields.ChunkChars; + var previousField = MoveChunksFields.ChunkPrevious; + var lengthField = MoveChunksFields.ChunkLength; + var offsetField = MoveChunksFields.ChunkOffset; + var maxCapacity = (int) MoveChunksFields.MaxCapacity.GetValue(source)!; + var destination = new StringBuilder(0, maxCapacity); + charsField.SetValue(destination, charsField.GetValue(source)); + previousField.SetValue(destination, previousField.GetValue(source)); + lengthField.SetValue(destination, lengthField.GetValue(source)); + offsetField.SetValue(destination, offsetField.GetValue(source)); + charsField.SetValue(source, Array.Empty()); + previousField.SetValue(source, null); + lengthField.SetValue(source, 0); + offsetField.SetValue(source, 0); + return destination; + } + } + static class MoveChunksFields + { + internal static readonly FieldInfo ChunkChars = GetField("m_ChunkChars"); + internal static readonly FieldInfo ChunkPrevious = GetField("m_ChunkPrevious"); + internal static readonly FieldInfo ChunkLength = GetField("m_ChunkLength"); + internal static readonly FieldInfo ChunkOffset = GetField("m_ChunkOffset"); + internal static readonly FieldInfo MaxCapacity = GetField("m_MaxCapacity"); + static FieldInfo GetField(string name) => + typeof(StringBuilder).GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ?? + throw new($"Expected to find field '{name}' on StringBuilder"); + } +} +#endif diff --git a/src/Tests/PolyfillTests_StringBuilder.cs b/src/Tests/PolyfillTests_StringBuilder.cs index 800b8a7b..ad42616d 100644 --- a/src/Tests/PolyfillTests_StringBuilder.cs +++ b/src/Tests/PolyfillTests_StringBuilder.cs @@ -90,6 +90,29 @@ public async Task AppendLineWithFormat() await Assert.That(builder.ToString()).IsEqualTo("value10" + Environment.NewLine); } + [Test] + public async Task MoveChunks() + { + var source = new StringBuilder("hello", 10); + var maxCapacity = source.MaxCapacity; + + var destination = StringBuilder.MoveChunks(source); + + await Assert.That(destination.ToString()).IsEqualTo("hello"); + await Assert.That(destination.MaxCapacity).IsEqualTo(maxCapacity); + + await Assert.That(source.Length).IsEqualTo(0); + await Assert.That(source.Capacity).IsEqualTo(0); + await Assert.That(source.MaxCapacity).IsEqualTo(maxCapacity); + + source.Append('x'); + await Assert.That(source.ToString()).IsEqualTo("x"); + } + + [Test] + public async Task MoveChunks_Null_Throws() => + await Assert.That(() => StringBuilder.MoveChunks(null!)).Throws(); + [Test] public async Task StringBuilder_Insert_ReadOnlySpan() {