diff --git a/apiCount.include.md b/apiCount.include.md index 7149901f..b1c1c59d 100644 --- a/apiCount.include.md +++ b/apiCount.include.md @@ -1,28 +1,28 @@ -**API count: 1105** +**API count: 1109** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1032 | -| `net462` | 1032 | -| `net47` | 1031 | -| `net471` | 1030 | -| `net472` | 1026 | -| `net48` | 1026 | -| `net481` | 1026 | -| `netstandard2.0` | 1028 | -| `netstandard2.1` | 881 | -| `netcoreapp2.0` | 951 | -| `netcoreapp2.1` | 892 | -| `netcoreapp2.2` | 892 | -| `netcoreapp3.0` | 853 | -| `netcoreapp3.1` | 852 | -| `net5.0` | 722 | -| `net6.0` | 623 | -| `net7.0` | 474 | -| `net8.0` | 353 | +| `net461` | 1036 | +| `net462` | 1036 | +| `net47` | 1035 | +| `net471` | 1034 | +| `net472` | 1030 | +| `net48` | 1030 | +| `net481` | 1030 | +| `netstandard2.0` | 1032 | +| `netstandard2.1` | 885 | +| `netcoreapp2.0` | 955 | +| `netcoreapp2.1` | 896 | +| `netcoreapp2.2` | 896 | +| `netcoreapp3.0` | 857 | +| `netcoreapp3.1` | 856 | +| `net5.0` | 726 | +| `net6.0` | 627 | +| `net7.0` | 478 | +| `net8.0` | 357 | | `net9.0` | 236 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1018 | +| `uap10.0` | 1022 | diff --git a/api_list.include.md b/api_list.include.md index d0ca7ab5..668d8752 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -1473,6 +1473,14 @@ #### Uri + * `string EscapeDataString(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.escapedatastring?view=net-11.0#system-uri-escapedatastring(system-readonlyspan((system-char)))) + * Note: Copies the span to a string first, so this allocates where the BCL escapes straight from the span. + * `bool TryEscapeDataString(ReadOnlySpan, Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.tryescapedatastring?view=net-11.0) + * Note: Escapes through an intermediate string, so this allocates where the BCL writes straight to the destination. + * `bool TryUnescapeDataString(ReadOnlySpan, Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.tryunescapedatastring?view=net-11.0) + * Note: Unescapes through an intermediate string, so this allocates where the BCL writes straight to the destination. + * `string UnescapeDataString(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.unescapedatastring?view=net-11.0#system-uri-unescapedatastring(system-readonlyspan((system-char)))) + * Note: Copies the span to a string first, so this allocates where the BCL unescapes straight from the span. * `string UriSchemeData` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.urischemedata?view=net-11.0) diff --git a/assemblySize.include.md b/assemblySize.include.md index e3799e63..7fddf141 100644 --- a/assemblySize.include.md +++ b/assemblySize.include.md @@ -2,24 +2,24 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 370.5KB | +362.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 326.0KB | +317.5KB | +8.5KB | +6.5KB | +8.5KB | +13.5KB | -| net461 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 374.0KB | +367.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net47 | 7.0KB | 374.0KB | +367.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net471 | 8.5KB | 371.5KB | +363.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net472 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net48 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net481 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| netcoreapp2.0 | 9.0KB | 349.0KB | +340.0KB | +8.0KB | +7.0KB | +9.5KB | +14.0KB | -| netcoreapp2.1 | 9.0KB | 329.5KB | +320.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 329.5KB | +320.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 325.5KB | +316.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 323.5KB | +314.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net5.0 | 9.5KB | 287.5KB | +278.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net6.0 | 10.0KB | 229.0KB | +219.0KB | +10.0KB | +7.0KB | +512bytes | +3.5KB | -| net7.0 | 10.0KB | 195.5KB | +185.5KB | +9.5KB | +5.5KB | +512bytes | +3.5KB | -| net8.0 | 9.5KB | 165.5KB | +156.0KB | +8.5KB | | +512bytes | +3.5KB | +| netstandard2.0 | 8.0KB | 371.0KB | +363.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 326.5KB | +318.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 370.0KB | +361.5KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net462 | 7.0KB | 375.0KB | +368.0KB | +7.0KB | +6.5KB | +7.5KB | +11.5KB | +| net47 | 7.0KB | 374.5KB | +367.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 372.5KB | +364.0KB | +8.5KB | +6.0KB | +8.5KB | +13.0KB | +| net472 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 350.0KB | +341.0KB | +7.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.1 | 9.0KB | 330.0KB | +321.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 330.0KB | +321.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 326.0KB | +316.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 324.0KB | +314.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net5.0 | 9.5KB | 288.0KB | +278.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net6.0 | 10.0KB | 230.0KB | +220.0KB | +9.5KB | +6.5KB | +512bytes | +3.0KB | +| net7.0 | 10.0KB | 196.0KB | +186.0KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 166.0KB | +156.5KB | +8.5KB | +512bytes | +512bytes | +3.5KB | | net9.0 | 9.5KB | 115.5KB | +106.0KB | +8.5KB | | +512bytes | +3.0KB | | net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -29,24 +29,24 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 543.3KB | +535.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 472.2KB | +463.7KB | +16.2KB | +8.2KB | +13.4KB | +18.9KB | -| net461 | 8.5KB | 542.8KB | +534.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 547.8KB | +540.8KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net47 | 7.0KB | 547.6KB | +540.6KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net471 | 8.5KB | 544.7KB | +536.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net472 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net48 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net481 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| netcoreapp2.0 | 9.0KB | 511.2KB | +502.2KB | +15.7KB | +8.7KB | +14.4KB | +19.4KB | -| netcoreapp2.1 | 9.0KB | 479.4KB | +470.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 479.4KB | +470.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 468.7KB | +459.2KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 466.7KB | +457.2KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net5.0 | 9.5KB | 412.6KB | +403.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net6.0 | 10.0KB | 334.1KB | +324.1KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB | -| net7.0 | 10.0KB | 282.8KB | +272.8KB | +17.1KB | +6.9KB | +1.1KB | +4.2KB | -| net8.0 | 9.5KB | 239.0KB | +229.5KB | +16.0KB | +299bytes | +1.1KB | +4.2KB | +| netstandard2.0 | 8.0KB | 544.1KB | +536.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 473.0KB | +464.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 544.1KB | +535.6KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net462 | 7.0KB | 549.1KB | +542.1KB | +14.7KB | +8.2KB | +12.4KB | +16.9KB | +| net47 | 7.0KB | 548.4KB | +541.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 546.0KB | +537.5KB | +16.2KB | +7.7KB | +13.4KB | +18.4KB | +| net472 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 512.5KB | +503.5KB | +15.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.1 | 9.0KB | 480.2KB | +471.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 480.2KB | +471.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 469.5KB | +460.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 467.5KB | +458.0KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net5.0 | 9.5KB | 413.4KB | +403.9KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net6.0 | 10.0KB | 335.4KB | +325.4KB | +17.2KB | +8.2KB | +1.1KB | +3.7KB | +| net7.0 | 10.0KB | 283.6KB | +273.6KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 239.8KB | +230.3KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | | net9.0 | 9.5KB | 166.8KB | +157.3KB | +16.0KB | | +1.1KB | +3.7KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | diff --git a/readme.md b/readme.md index 45d6bcde..e40fa0f2 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: 1105** +**API count: 1109** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1032 | -| `net462` | 1032 | -| `net47` | 1031 | -| `net471` | 1030 | -| `net472` | 1026 | -| `net48` | 1026 | -| `net481` | 1026 | -| `netstandard2.0` | 1028 | -| `netstandard2.1` | 881 | -| `netcoreapp2.0` | 951 | -| `netcoreapp2.1` | 892 | -| `netcoreapp2.2` | 892 | -| `netcoreapp3.0` | 853 | -| `netcoreapp3.1` | 852 | -| `net5.0` | 722 | -| `net6.0` | 623 | -| `net7.0` | 474 | -| `net8.0` | 353 | +| `net461` | 1036 | +| `net462` | 1036 | +| `net47` | 1035 | +| `net471` | 1034 | +| `net472` | 1030 | +| `net48` | 1030 | +| `net481` | 1030 | +| `netstandard2.0` | 1032 | +| `netstandard2.1` | 885 | +| `netcoreapp2.0` | 955 | +| `netcoreapp2.1` | 896 | +| `netcoreapp2.2` | 896 | +| `netcoreapp3.0` | 857 | +| `netcoreapp3.1` | 856 | +| `net5.0` | 726 | +| `net6.0` | 627 | +| `net7.0` | 478 | +| `net8.0` | 357 | | `net9.0` | 236 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1018 | +| `uap10.0` | 1022 | @@ -96,24 +96,24 @@ This project uses features from the newest stable SDK and C# language. As such c | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 370.5KB | +362.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 326.0KB | +317.5KB | +8.5KB | +6.5KB | +8.5KB | +13.5KB | -| net461 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 374.0KB | +367.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net47 | 7.0KB | 374.0KB | +367.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net471 | 8.5KB | 371.5KB | +363.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net472 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net48 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net481 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| netcoreapp2.0 | 9.0KB | 349.0KB | +340.0KB | +8.0KB | +7.0KB | +9.5KB | +14.0KB | -| netcoreapp2.1 | 9.0KB | 329.5KB | +320.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 329.5KB | +320.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 325.5KB | +316.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 323.5KB | +314.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net5.0 | 9.5KB | 287.5KB | +278.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net6.0 | 10.0KB | 229.0KB | +219.0KB | +10.0KB | +7.0KB | +512bytes | +3.5KB | -| net7.0 | 10.0KB | 195.5KB | +185.5KB | +9.5KB | +5.5KB | +512bytes | +3.5KB | -| net8.0 | 9.5KB | 165.5KB | +156.0KB | +8.5KB | | +512bytes | +3.5KB | +| netstandard2.0 | 8.0KB | 371.0KB | +363.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 326.5KB | +318.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 370.0KB | +361.5KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net462 | 7.0KB | 375.0KB | +368.0KB | +7.0KB | +6.5KB | +7.5KB | +11.5KB | +| net47 | 7.0KB | 374.5KB | +367.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 372.5KB | +364.0KB | +8.5KB | +6.0KB | +8.5KB | +13.0KB | +| net472 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 371.0KB | +362.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 350.0KB | +341.0KB | +7.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.1 | 9.0KB | 330.0KB | +321.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 330.0KB | +321.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 326.0KB | +316.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 324.0KB | +314.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net5.0 | 9.5KB | 288.0KB | +278.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net6.0 | 10.0KB | 230.0KB | +220.0KB | +9.5KB | +6.5KB | +512bytes | +3.0KB | +| net7.0 | 10.0KB | 196.0KB | +186.0KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 166.0KB | +156.5KB | +8.5KB | +512bytes | +512bytes | +3.5KB | | net9.0 | 9.5KB | 115.5KB | +106.0KB | +8.5KB | | +512bytes | +3.0KB | | net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -123,24 +123,24 @@ This project uses features from the newest stable SDK and C# language. As such c | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 543.3KB | +535.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 472.2KB | +463.7KB | +16.2KB | +8.2KB | +13.4KB | +18.9KB | -| net461 | 8.5KB | 542.8KB | +534.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 547.8KB | +540.8KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net47 | 7.0KB | 547.6KB | +540.6KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net471 | 8.5KB | 544.7KB | +536.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net472 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net48 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net481 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| netcoreapp2.0 | 9.0KB | 511.2KB | +502.2KB | +15.7KB | +8.7KB | +14.4KB | +19.4KB | -| netcoreapp2.1 | 9.0KB | 479.4KB | +470.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 479.4KB | +470.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 468.7KB | +459.2KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 466.7KB | +457.2KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net5.0 | 9.5KB | 412.6KB | +403.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net6.0 | 10.0KB | 334.1KB | +324.1KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB | -| net7.0 | 10.0KB | 282.8KB | +272.8KB | +17.1KB | +6.9KB | +1.1KB | +4.2KB | -| net8.0 | 9.5KB | 239.0KB | +229.5KB | +16.0KB | +299bytes | +1.1KB | +4.2KB | +| netstandard2.0 | 8.0KB | 544.1KB | +536.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 473.0KB | +464.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 544.1KB | +535.6KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net462 | 7.0KB | 549.1KB | +542.1KB | +14.7KB | +8.2KB | +12.4KB | +16.9KB | +| net47 | 7.0KB | 548.4KB | +541.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 546.0KB | +537.5KB | +16.2KB | +7.7KB | +13.4KB | +18.4KB | +| net472 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 543.4KB | +534.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 512.5KB | +503.5KB | +15.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.1 | 9.0KB | 480.2KB | +471.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 480.2KB | +471.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 469.5KB | +460.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 467.5KB | +458.0KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net5.0 | 9.5KB | 413.4KB | +403.9KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net6.0 | 10.0KB | 335.4KB | +325.4KB | +17.2KB | +8.2KB | +1.1KB | +3.7KB | +| net7.0 | 10.0KB | 283.6KB | +273.6KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 239.8KB | +230.3KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | | net9.0 | 9.5KB | 166.8KB | +157.3KB | +16.0KB | | +1.1KB | +3.7KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | @@ -2088,6 +2088,14 @@ The class `Polyfill` includes the following extension methods: #### Uri + * `string EscapeDataString(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.escapedatastring?view=net-11.0#system-uri-escapedatastring(system-readonlyspan((system-char)))) + * Note: Copies the span to a string first, so this allocates where the BCL escapes straight from the span. + * `bool TryEscapeDataString(ReadOnlySpan, Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.tryescapedatastring?view=net-11.0) + * Note: Escapes through an intermediate string, so this allocates where the BCL writes straight to the destination. + * `bool TryUnescapeDataString(ReadOnlySpan, Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.tryunescapedatastring?view=net-11.0) + * Note: Unescapes through an intermediate string, so this allocates where the BCL writes straight to the destination. + * `string UnescapeDataString(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.unescapedatastring?view=net-11.0#system-uri-unescapedatastring(system-readonlyspan((system-char)))) + * Note: Copies the span to a string first, so this allocates where the BCL unescapes straight from the span. * `string UriSchemeData` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uri.urischemedata?view=net-11.0) @@ -2489,7 +2497,7 @@ void ObjectDisposedExceptionExample(bool isDisposed) ObjectDisposedException.ThrowIf(isDisposed, typeof(Consume)); } ``` -snippet source | anchor +snippet source | anchor @@ -2508,7 +2516,7 @@ void EnsureExample(Order order, Customer customer, string customerId, string ema this.quantity = Ensure.NotNegativeOrZero(quantity); } ``` -snippet source | anchor +snippet source | anchor diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index 9a29574e..f9df0ab2 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -331,6 +331,17 @@ void UriSchemeDataUsage() _ = Uri.UriSchemeData; } +#if FeatureMemory + void UriEscapingUsage() + { + var escaped = Uri.EscapeDataString("a b".AsSpan()); + var unescaped = Uri.UnescapeDataString("a%20b".AsSpan()); + Span destination = stackalloc char[10]; + var escapedOk = Uri.TryEscapeDataString("a b".AsSpan(), destination, out var escapedWritten); + var unescapedOk = Uri.TryUnescapeDataString("a%20b".AsSpan(), destination, out var unescapedWritten); + } +#endif + #if FeatureMemory void Utf16Usage() { diff --git a/src/Polyfill/UriPolyfill.cs b/src/Polyfill/UriPolyfill.cs index 3802c397..7793a334 100644 --- a/src/Polyfill/UriPolyfill.cs +++ b/src/Polyfill/UriPolyfill.cs @@ -13,7 +13,60 @@ static partial class Polyfill /// //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uri.urischemedata?view=net-11.0 public static string UriSchemeData => "data"; + +#if FeatureMemory && !NET9_0_OR_GREATER + + /// + /// Converts a span of characters to its escaped representation. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uri.escapedatastring?view=net-11.0#system-uri-escapedatastring(system-readonlyspan((system-char))) + //Note: Copies the span to a string first, so this allocates where the BCL escapes straight from the span. + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + + /// + /// Converts a span of characters to its unescaped representation. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uri.unescapedatastring?view=net-11.0#system-uri-unescapedatastring(system-readonlyspan((system-char))) + //Note: Copies the span to a string first, so this allocates where the BCL unescapes straight from the span. + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uri.tryescapedatastring?view=net-11.0 + //Note: Escapes through an intermediate string, so this allocates where the BCL writes straight to the destination. + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uri.tryunescapedatastring?view=net-11.0 + //Note: Unescapes through an intermediate string, so this allocates where the BCL writes straight to the destination. + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); + +#endif } + +#if FeatureMemory && !NET9_0_OR_GREATER + + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; + } + +#endif } #endif diff --git a/src/Split/net461/UriPolyfill.cs b/src/Split/net461/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net461/UriPolyfill.cs +++ b/src/Split/net461/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net462/UriPolyfill.cs b/src/Split/net462/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net462/UriPolyfill.cs +++ b/src/Split/net462/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net47/UriPolyfill.cs b/src/Split/net47/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net47/UriPolyfill.cs +++ b/src/Split/net47/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net471/UriPolyfill.cs b/src/Split/net471/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net471/UriPolyfill.cs +++ b/src/Split/net471/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net472/UriPolyfill.cs b/src/Split/net472/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net472/UriPolyfill.cs +++ b/src/Split/net472/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net48/UriPolyfill.cs b/src/Split/net48/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net48/UriPolyfill.cs +++ b/src/Split/net48/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net481/UriPolyfill.cs b/src/Split/net481/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net481/UriPolyfill.cs +++ b/src/Split/net481/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net5.0/UriPolyfill.cs b/src/Split/net5.0/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net5.0/UriPolyfill.cs +++ b/src/Split/net5.0/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net6.0/UriPolyfill.cs b/src/Split/net6.0/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net6.0/UriPolyfill.cs +++ b/src/Split/net6.0/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net7.0/UriPolyfill.cs b/src/Split/net7.0/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net7.0/UriPolyfill.cs +++ b/src/Split/net7.0/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/net8.0/UriPolyfill.cs b/src/Split/net8.0/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/net8.0/UriPolyfill.cs +++ b/src/Split/net8.0/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/netcoreapp2.0/UriPolyfill.cs b/src/Split/netcoreapp2.0/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/netcoreapp2.0/UriPolyfill.cs +++ b/src/Split/netcoreapp2.0/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/netcoreapp2.1/UriPolyfill.cs b/src/Split/netcoreapp2.1/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/netcoreapp2.1/UriPolyfill.cs +++ b/src/Split/netcoreapp2.1/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/netcoreapp2.2/UriPolyfill.cs b/src/Split/netcoreapp2.2/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/netcoreapp2.2/UriPolyfill.cs +++ b/src/Split/netcoreapp2.2/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/netcoreapp3.0/UriPolyfill.cs b/src/Split/netcoreapp3.0/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/netcoreapp3.0/UriPolyfill.cs +++ b/src/Split/netcoreapp3.0/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/netcoreapp3.1/UriPolyfill.cs b/src/Split/netcoreapp3.1/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/netcoreapp3.1/UriPolyfill.cs +++ b/src/Split/netcoreapp3.1/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/netstandard2.0/UriPolyfill.cs b/src/Split/netstandard2.0/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/netstandard2.0/UriPolyfill.cs +++ b/src/Split/netstandard2.0/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/netstandard2.1/UriPolyfill.cs b/src/Split/netstandard2.1/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/netstandard2.1/UriPolyfill.cs +++ b/src/Split/netstandard2.1/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Split/uap10.0/UriPolyfill.cs b/src/Split/uap10.0/UriPolyfill.cs index 81679bd1..88311079 100644 --- a/src/Split/uap10.0/UriPolyfill.cs +++ b/src/Split/uap10.0/UriPolyfill.cs @@ -10,5 +10,40 @@ static partial class Polyfill /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. /// public static string UriSchemeData => "data"; +#if FeatureMemory + /// + /// Converts a span of characters to its escaped representation. + /// + public static string EscapeDataString(ReadOnlySpan charsToEscape) => + Uri.EscapeDataString(charsToEscape.ToString()); + /// + /// Converts a span of characters to its unescaped representation. + /// + public static string UnescapeDataString(ReadOnlySpan charsToUnescape) => + Uri.UnescapeDataString(charsToUnescape.ToString()); + /// + /// Attempts to convert a span of characters to its escaped representation. + /// + public static bool TryEscapeDataString(ReadOnlySpan charsToEscape, Span destination, out int charsWritten) => + TryWriteTo(Uri.EscapeDataString(charsToEscape.ToString()), destination, out charsWritten); + /// + /// Attempts to convert a span of characters to its unescaped representation. + /// + public static bool TryUnescapeDataString(ReadOnlySpan charsToUnescape, Span destination, out int charsWritten) => + TryWriteTo(Uri.UnescapeDataString(charsToUnescape.ToString()), destination, out charsWritten); +#endif + } +#if FeatureMemory + static bool TryWriteTo(string value, Span destination, out int charsWritten) + { + if (value.Length > destination.Length) + { + charsWritten = 0; + return false; + } + value.AsSpan().CopyTo(destination); + charsWritten = value.Length; + return true; } +#endif } diff --git a/src/Tests/UriPolyfillTests.cs b/src/Tests/UriPolyfillTests.cs new file mode 100644 index 00000000..8d04f9c5 --- /dev/null +++ b/src/Tests/UriPolyfillTests.cs @@ -0,0 +1,179 @@ +#if FeatureMemory + +// The span overloads must agree with the string overloads on the framework they run on. +// Uri.EscapeDataString itself escapes a different set of characters on .NET Framework than +// on .NET, so comparing the two overloads is the invariant that holds everywhere, rather +// than any fixed expected output. +public class UriPolyfillTests +{ + static string[] samples = + [ + "", + "abc", + "a b", + "a/b?c=d&e=f", + "%", + "%%", + "%4", + "%41", + "%zz", + "%25", + "-._~", + "!*'()", + "+", + "é", + "你好", + "%E4%BD%A0%E5%A5%BD", + "abc%20def" + ]; + + [Test] + public async Task EscapeDataString_MatchesStringOverload() + { + foreach (var sample in samples) + { + var fromSpan = Capture(() => Uri.EscapeDataString(sample.AsSpan())); + var fromString = Capture(() => Uri.EscapeDataString(sample)); + await Assert.That(fromSpan).IsEqualTo(fromString); + } + } + + [Test] + public async Task UnescapeDataString_MatchesStringOverload() + { + foreach (var sample in samples) + { + var fromSpan = Capture(() => Uri.UnescapeDataString(sample.AsSpan())); + var fromString = Capture(() => Uri.UnescapeDataString(sample)); + await Assert.That(fromSpan).IsEqualTo(fromString); + } + } + + [Test] + public async Task EscapeDataString_KnownValues() + { + await Assert.That(Uri.EscapeDataString("a b".AsSpan())).IsEqualTo("a%20b"); + await Assert.That(Uri.EscapeDataString("abc".AsSpan())).IsEqualTo("abc"); + await Assert.That(Uri.EscapeDataString(default(ReadOnlySpan))).IsEqualTo(string.Empty); + await Assert.That(Uri.UnescapeDataString("a%20b".AsSpan())).IsEqualTo("a b"); + await Assert.That(Uri.UnescapeDataString(default(ReadOnlySpan))).IsEqualTo(string.Empty); + } + + [Test] + public async Task TryEscapeDataString_Sizing() + { + foreach (var sample in samples) + { + var expected = Uri.EscapeDataString(sample); + for (var size = 0; size <= expected.Length + 1; size++) + { + var result = TryEscape(sample, size); + + await Assert.That(result.Succeeded).IsEqualTo(size >= expected.Length); + if (result.Succeeded) + { + await Assert.That(result.Written).IsEqualTo(expected.Length); + await Assert.That(result.Content).IsEqualTo(expected); + } + else + { + // a failed attempt reports nothing written + await Assert.That(result.Written).IsEqualTo(0); + } + } + } + } + + [Test] + public async Task TryUnescapeDataString_Sizing() + { + foreach (var sample in samples) + { + var expected = Uri.UnescapeDataString(sample); + for (var size = SmallestTestableSize(sample); size <= expected.Length + 1; size++) + { + var result = TryUnescape(sample, size); + + await Assert.That(result.Succeeded).IsEqualTo(size >= expected.Length); + if (result.Succeeded) + { + await Assert.That(result.Written).IsEqualTo(expected.Length); + await Assert.That(result.Content).IsEqualTo(expected); + } + else + { + await Assert.That(result.Written).IsEqualTo(0); + } + } + } + } + + // net9.0 and net10.0 throw ArgumentOutOfRangeException instead of returning false when the + // destination cannot hold the literal text preceding the first escape sequence. Fixed in + // net11, and the polyfill behaves the way net11 does. + [Test] + public async Task TryUnescapeDataString_DestinationSmallerThanLiteralPrefix() + { +#if NET9_0_OR_GREATER && !NET11_0_OR_GREATER + await Assert.That(() => TryUnescape("abc%20def", 2)).Throws(); +#else + var result = TryUnescape("abc%20def", 2); + await Assert.That(result.Succeeded).IsFalse(); + await Assert.That(result.Written).IsEqualTo(0); +#endif + } + + // unescaping only ever shrinks, so a destination that overlaps the source is safe + [Test] + public async Task TryUnescapeDataString_InPlace() + { + var buffer = "a%20b".ToCharArray(); + var succeeded = Uri.TryUnescapeDataString(buffer, buffer, out var written); + var writtenCount = written; + var content = new string(buffer, 0, written); + + await Assert.That(succeeded).IsTrue(); + await Assert.That(writtenCount).IsEqualTo(3); + await Assert.That(content).IsEqualTo("a b"); + } + + static int SmallestTestableSize(string sample) + { +#if NET9_0_OR_GREATER && !NET11_0_OR_GREATER + // skip the sizes covered by TryUnescapeDataString_DestinationSmallerThanLiteralPrefix + var index = sample.IndexOf('%'); + return index < 0 ? 0 : index; +#else + return 0; +#endif + } + + // the spans stay inside these helpers, so nothing ref struct shaped has to live across an await + static (bool Succeeded, int Written, string? Content) TryEscape(string value, int destinationSize) + { + var buffer = new char[destinationSize]; + var succeeded = Uri.TryEscapeDataString(value.AsSpan(), buffer, out var written); + return (succeeded, written, succeeded ? new string(buffer, 0, written) : null); + } + + static (bool Succeeded, int Written, string? Content) TryUnescape(string value, int destinationSize) + { + var buffer = new char[destinationSize]; + var succeeded = Uri.TryUnescapeDataString(value.AsSpan(), buffer, out var written); + return (succeeded, written, succeeded ? new string(buffer, 0, written) : null); + } + + static string Capture(Func func) + { + try + { + return func(); + } + catch (Exception exception) + { + return exception.GetType().Name; + } + } +} + +#endif