From 01596c6008a573d6db4fdc35c1526ee635685f05 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 10 Jun 2026 14:15:43 +1000 Subject: [PATCH 1/2] Add .NET 11 Base64, Utf8/Utf16 validation and Uri.UriSchemeData polyfills - Base64: EncodeToChars/EncodeToString/EncodeToUtf8 and DecodeFromChars/DecodeFromUtf8 (+ Try variants, GetEncodedLength, GetMaxDecodedLength) via extension(Base64) - Utf16: recreated type with IsValid and IndexOfInvalidSubsequence - Utf8.IndexOfInvalidSubsequence via extension(Utf8) - Uri.UriSchemeData Base64/Utf8 extensions gate !RefsBclMemory (the type also lives in Microsoft.Bcl.Memory); Utf16 is recreated everywhere FeatureMemory is available. Adds tests, Consume usage, and regenerated Split + api_list. --- readme.md | 133 +++++++------ src/Consume/Consume.cs | 2 +- src/Polyfill/Base64Polyfill.cs | 221 +++++++++++++++++++++ src/Polyfill/UriPolyfill.cs | 19 ++ src/Polyfill/Utf16.cs | 66 ++++++ src/Polyfill/Utf8Polyfill.cs | 109 ++++++++++ src/Split/net10.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net10.0/UriPolyfill.cs | 14 ++ src/Split/net10.0/Utf16.cs | 50 +++++ src/Split/net10.0/Utf8Polyfill.cs | 95 +++++++++ src/Split/net461/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net461/UriPolyfill.cs | 14 ++ src/Split/net461/Utf16.cs | 50 +++++ src/Split/net462/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net462/UriPolyfill.cs | 14 ++ src/Split/net462/Utf16.cs | 50 +++++ src/Split/net47/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net47/UriPolyfill.cs | 14 ++ src/Split/net47/Utf16.cs | 50 +++++ src/Split/net471/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net471/UriPolyfill.cs | 14 ++ src/Split/net471/Utf16.cs | 50 +++++ src/Split/net472/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net472/UriPolyfill.cs | 14 ++ src/Split/net472/Utf16.cs | 50 +++++ src/Split/net48/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net48/UriPolyfill.cs | 14 ++ src/Split/net48/Utf16.cs | 50 +++++ src/Split/net481/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net481/UriPolyfill.cs | 14 ++ src/Split/net481/Utf16.cs | 50 +++++ src/Split/net5.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net5.0/UriPolyfill.cs | 14 ++ src/Split/net5.0/Utf16.cs | 50 +++++ src/Split/net5.0/Utf8Polyfill.cs | 95 +++++++++ src/Split/net6.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net6.0/UriPolyfill.cs | 14 ++ src/Split/net6.0/Utf16.cs | 50 +++++ src/Split/net6.0/Utf8Polyfill.cs | 95 +++++++++ src/Split/net7.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net7.0/UriPolyfill.cs | 14 ++ src/Split/net7.0/Utf16.cs | 50 +++++ src/Split/net7.0/Utf8Polyfill.cs | 95 +++++++++ src/Split/net8.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net8.0/UriPolyfill.cs | 14 ++ src/Split/net8.0/Utf16.cs | 50 +++++ src/Split/net8.0/Utf8Polyfill.cs | 95 +++++++++ src/Split/net9.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/net9.0/UriPolyfill.cs | 14 ++ src/Split/net9.0/Utf16.cs | 50 +++++ src/Split/net9.0/Utf8Polyfill.cs | 95 +++++++++ src/Split/netcoreapp2.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/netcoreapp2.0/UriPolyfill.cs | 14 ++ src/Split/netcoreapp2.0/Utf16.cs | 50 +++++ src/Split/netcoreapp2.1/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/netcoreapp2.1/UriPolyfill.cs | 14 ++ src/Split/netcoreapp2.1/Utf16.cs | 50 +++++ src/Split/netcoreapp2.2/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/netcoreapp2.2/UriPolyfill.cs | 14 ++ src/Split/netcoreapp2.2/Utf16.cs | 50 +++++ src/Split/netcoreapp3.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/netcoreapp3.0/UriPolyfill.cs | 14 ++ src/Split/netcoreapp3.0/Utf16.cs | 50 +++++ src/Split/netcoreapp3.0/Utf8Polyfill.cs | 95 +++++++++ src/Split/netcoreapp3.1/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/netcoreapp3.1/UriPolyfill.cs | 14 ++ src/Split/netcoreapp3.1/Utf16.cs | 50 +++++ src/Split/netcoreapp3.1/Utf8Polyfill.cs | 95 +++++++++ src/Split/netstandard2.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/netstandard2.0/UriPolyfill.cs | 14 ++ src/Split/netstandard2.0/Utf16.cs | 50 +++++ src/Split/netstandard2.1/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/netstandard2.1/UriPolyfill.cs | 14 ++ src/Split/netstandard2.1/Utf16.cs | 50 +++++ src/Split/uap10.0/Base64Polyfill.cs | 173 ++++++++++++++++ src/Split/uap10.0/UriPolyfill.cs | 14 ++ src/Split/uap10.0/Utf16.cs | 50 +++++ src/Tests/Base64Tests.cs | 161 +++++++++++++++ src/Tests/UriTests.cs | 11 + src/Tests/Utf16Tests.cs | 58 ++++++ src/Tests/Utf8Tests.cs | 60 ++++++ 81 files changed, 6518 insertions(+), 59 deletions(-) create mode 100644 src/Polyfill/Base64Polyfill.cs create mode 100644 src/Polyfill/UriPolyfill.cs create mode 100644 src/Polyfill/Utf16.cs create mode 100644 src/Polyfill/Utf8Polyfill.cs create mode 100644 src/Split/net10.0/Base64Polyfill.cs create mode 100644 src/Split/net10.0/UriPolyfill.cs create mode 100644 src/Split/net10.0/Utf16.cs create mode 100644 src/Split/net10.0/Utf8Polyfill.cs create mode 100644 src/Split/net461/Base64Polyfill.cs create mode 100644 src/Split/net461/UriPolyfill.cs create mode 100644 src/Split/net461/Utf16.cs create mode 100644 src/Split/net462/Base64Polyfill.cs create mode 100644 src/Split/net462/UriPolyfill.cs create mode 100644 src/Split/net462/Utf16.cs create mode 100644 src/Split/net47/Base64Polyfill.cs create mode 100644 src/Split/net47/UriPolyfill.cs create mode 100644 src/Split/net47/Utf16.cs create mode 100644 src/Split/net471/Base64Polyfill.cs create mode 100644 src/Split/net471/UriPolyfill.cs create mode 100644 src/Split/net471/Utf16.cs create mode 100644 src/Split/net472/Base64Polyfill.cs create mode 100644 src/Split/net472/UriPolyfill.cs create mode 100644 src/Split/net472/Utf16.cs create mode 100644 src/Split/net48/Base64Polyfill.cs create mode 100644 src/Split/net48/UriPolyfill.cs create mode 100644 src/Split/net48/Utf16.cs create mode 100644 src/Split/net481/Base64Polyfill.cs create mode 100644 src/Split/net481/UriPolyfill.cs create mode 100644 src/Split/net481/Utf16.cs create mode 100644 src/Split/net5.0/Base64Polyfill.cs create mode 100644 src/Split/net5.0/UriPolyfill.cs create mode 100644 src/Split/net5.0/Utf16.cs create mode 100644 src/Split/net5.0/Utf8Polyfill.cs create mode 100644 src/Split/net6.0/Base64Polyfill.cs create mode 100644 src/Split/net6.0/UriPolyfill.cs create mode 100644 src/Split/net6.0/Utf16.cs create mode 100644 src/Split/net6.0/Utf8Polyfill.cs create mode 100644 src/Split/net7.0/Base64Polyfill.cs create mode 100644 src/Split/net7.0/UriPolyfill.cs create mode 100644 src/Split/net7.0/Utf16.cs create mode 100644 src/Split/net7.0/Utf8Polyfill.cs create mode 100644 src/Split/net8.0/Base64Polyfill.cs create mode 100644 src/Split/net8.0/UriPolyfill.cs create mode 100644 src/Split/net8.0/Utf16.cs create mode 100644 src/Split/net8.0/Utf8Polyfill.cs create mode 100644 src/Split/net9.0/Base64Polyfill.cs create mode 100644 src/Split/net9.0/UriPolyfill.cs create mode 100644 src/Split/net9.0/Utf16.cs create mode 100644 src/Split/net9.0/Utf8Polyfill.cs create mode 100644 src/Split/netcoreapp2.0/Base64Polyfill.cs create mode 100644 src/Split/netcoreapp2.0/UriPolyfill.cs create mode 100644 src/Split/netcoreapp2.0/Utf16.cs create mode 100644 src/Split/netcoreapp2.1/Base64Polyfill.cs create mode 100644 src/Split/netcoreapp2.1/UriPolyfill.cs create mode 100644 src/Split/netcoreapp2.1/Utf16.cs create mode 100644 src/Split/netcoreapp2.2/Base64Polyfill.cs create mode 100644 src/Split/netcoreapp2.2/UriPolyfill.cs create mode 100644 src/Split/netcoreapp2.2/Utf16.cs create mode 100644 src/Split/netcoreapp3.0/Base64Polyfill.cs create mode 100644 src/Split/netcoreapp3.0/UriPolyfill.cs create mode 100644 src/Split/netcoreapp3.0/Utf16.cs create mode 100644 src/Split/netcoreapp3.0/Utf8Polyfill.cs create mode 100644 src/Split/netcoreapp3.1/Base64Polyfill.cs create mode 100644 src/Split/netcoreapp3.1/UriPolyfill.cs create mode 100644 src/Split/netcoreapp3.1/Utf16.cs create mode 100644 src/Split/netcoreapp3.1/Utf8Polyfill.cs create mode 100644 src/Split/netstandard2.0/Base64Polyfill.cs create mode 100644 src/Split/netstandard2.0/UriPolyfill.cs create mode 100644 src/Split/netstandard2.0/Utf16.cs create mode 100644 src/Split/netstandard2.1/Base64Polyfill.cs create mode 100644 src/Split/netstandard2.1/UriPolyfill.cs create mode 100644 src/Split/netstandard2.1/Utf16.cs create mode 100644 src/Split/uap10.0/Base64Polyfill.cs create mode 100644 src/Split/uap10.0/UriPolyfill.cs create mode 100644 src/Split/uap10.0/Utf16.cs create mode 100644 src/Tests/Base64Tests.cs create mode 100644 src/Tests/UriTests.cs create mode 100644 src/Tests/Utf16Tests.cs create mode 100644 src/Tests/Utf8Tests.cs diff --git a/readme.md b/readme.md index 408e9c58..25271ead 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: 965** +**API count: 978** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 938 | -| `net462` | 938 | -| `net47` | 937 | -| `net471` | 936 | -| `net472` | 932 | -| `net48` | 932 | -| `net481` | 932 | -| `netstandard2.0` | 934 | -| `netstandard2.1` | 765 | -| `netcoreapp2.0` | 857 | -| `netcoreapp2.1` | 776 | -| `netcoreapp2.2` | 776 | -| `netcoreapp3.0` | 723 | -| `netcoreapp3.1` | 722 | -| `net5.0` | 594 | -| `net6.0` | 499 | -| `net7.0` | 346 | -| `net8.0` | 227 | +| `net461` | 951 | +| `net462` | 951 | +| `net47` | 950 | +| `net471` | 949 | +| `net472` | 945 | +| `net48` | 945 | +| `net481` | 945 | +| `netstandard2.0` | 947 | +| `netstandard2.1` | 778 | +| `netcoreapp2.0` | 870 | +| `netcoreapp2.1` | 789 | +| `netcoreapp2.2` | 789 | +| `netcoreapp3.0` | 736 | +| `netcoreapp3.1` | 735 | +| `net5.0` | 607 | +| `net6.0` | 512 | +| `net7.0` | 359 | +| `net8.0` | 240 | | `net9.0` | 151 | | `net10.0` | 98 | | `net11.0` | 58 | -| `uap10.0` | 924 | +| `uap10.0` | 937 | @@ -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 | 327.5KB | +319.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netstandard2.1 | 8.5KB | 281.0KB | +272.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net461 | 8.5KB | 326.0KB | +317.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net462 | 7.0KB | 330.0KB | +323.0KB | +8.5KB | +6.0KB | +9.0KB | +13.5KB | -| net47 | 7.0KB | 329.5KB | +322.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net471 | 8.5KB | 328.5KB | +320.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| net472 | 8.5KB | 327.5KB | +319.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 327.5KB | +319.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net481 | 8.5KB | 327.5KB | +319.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 303.5KB | +294.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp2.1 | 9.0KB | 283.5KB | +274.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.2 | 9.0KB | 283.5KB | +274.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.0 | 9.5KB | 276.0KB | +266.5KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp3.1 | 9.5KB | 274.5KB | +265.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net5.0 | 9.5KB | 237.5KB | +228.0KB | +9.5KB | +7.0KB | +9.5KB | +14.5KB | -| net6.0 | 10.0KB | 179.0KB | +169.0KB | +9.5KB | +7.0KB | +512bytes | +3.5KB | -| net7.0 | 10.0KB | 141.0KB | +131.0KB | +9.5KB | +6.0KB | +1.0KB | +4.0KB | -| net8.0 | 9.5KB | 112.5KB | +103.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB | +| netstandard2.0 | 8.0KB | 330.0KB | +322.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 283.5KB | +275.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 328.5KB | +320.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 332.0KB | +325.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net47 | 7.0KB | 332.0KB | +325.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net471 | 8.5KB | 331.0KB | +322.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net472 | 8.5KB | 330.0KB | +321.5KB | +8.5KB | +6.0KB | +8.5KB | +13.5KB | +| net48 | 8.5KB | 330.0KB | +321.5KB | +8.5KB | +6.0KB | +8.5KB | +13.5KB | +| net481 | 8.5KB | 330.0KB | +321.5KB | +8.5KB | +6.0KB | +8.5KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 306.0KB | +297.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.1 | 9.0KB | 286.0KB | +277.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp2.2 | 9.0KB | 286.0KB | +277.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| netcoreapp3.0 | 9.5KB | 278.5KB | +269.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 276.5KB | +267.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net5.0 | 9.5KB | 240.0KB | +230.5KB | +9.5KB | +6.5KB | +9.5KB | +14.0KB | +| net6.0 | 10.0KB | 181.5KB | +171.5KB | +10.0KB | +7.0KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 144.0KB | +134.0KB | +9.0KB | +5.0KB | +512bytes | +3.0KB | +| net8.0 | 9.5KB | 115.0KB | +105.5KB | +8.5KB | | +512bytes | +3.5KB | | net9.0 | 9.5KB | 69.0KB | +59.5KB | +8.5KB | | +512bytes | +3.5KB | | net10.0 | 10.0KB | 45.5KB | +35.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 20.5KB | +10.5KB | +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 | 477.7KB | +469.7KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netstandard2.1 | 8.5KB | 406.0KB | +397.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net461 | 8.5KB | 477.2KB | +468.7KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net462 | 7.0KB | 481.2KB | +474.2KB | +16.2KB | +7.7KB | +13.9KB | +18.9KB | -| net47 | 7.0KB | 480.5KB | +473.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net471 | 8.5KB | 479.1KB | +470.6KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| net472 | 8.5KB | 477.1KB | +468.6KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 477.1KB | +468.6KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net481 | 8.5KB | 477.1KB | +468.6KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 443.3KB | +434.3KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp2.1 | 9.0KB | 412.2KB | +403.2KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.2 | 9.0KB | 412.2KB | +403.2KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.0 | 9.5KB | 395.9KB | +386.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp3.1 | 9.5KB | 394.4KB | +384.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net5.0 | 9.5KB | 339.2KB | +329.7KB | +17.2KB | +8.7KB | +14.4KB | +19.9KB | -| net6.0 | 10.0KB | 260.7KB | +250.7KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB | -| net7.0 | 10.0KB | 204.1KB | +194.1KB | +17.1KB | +7.4KB | +1.6KB | +4.7KB | -| net8.0 | 9.5KB | 160.3KB | +150.8KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | +| netstandard2.0 | 8.0KB | 481.4KB | +473.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 409.6KB | +401.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 480.9KB | +472.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 484.4KB | +477.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net47 | 7.0KB | 484.2KB | +477.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net471 | 8.5KB | 482.8KB | +474.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net472 | 8.5KB | 480.8KB | +472.3KB | +16.2KB | +7.7KB | +13.4KB | +18.9KB | +| net48 | 8.5KB | 480.8KB | +472.3KB | +16.2KB | +7.7KB | +13.4KB | +18.9KB | +| net481 | 8.5KB | 480.8KB | +472.3KB | +16.2KB | +7.7KB | +13.4KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 447.0KB | +438.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.1 | 9.0KB | 415.9KB | +406.9KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp2.2 | 9.0KB | 415.9KB | +406.9KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| netcoreapp3.0 | 9.5KB | 399.6KB | +390.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 397.6KB | +388.1KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net5.0 | 9.5KB | 342.9KB | +333.4KB | +17.2KB | +8.2KB | +14.4KB | +19.4KB | +| net6.0 | 10.0KB | 264.4KB | +254.4KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 208.3KB | +198.3KB | +16.6KB | +6.4KB | +1.1KB | +3.7KB | +| net8.0 | 9.5KB | 164.0KB | +154.5KB | +16.0KB | +299bytes | +1.1KB | +4.2KB | | net9.0 | 9.5KB | 96.5KB | +87.0KB | +16.0KB | | +1.1KB | +4.2KB | | net10.0 | 10.0KB | 64.1KB | +54.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.3KB | +20.3KB | +16.5KB | | +1.1KB | +4.2KB | @@ -577,6 +577,23 @@ The class `Polyfill` includes the following extension methods: * `ArraySegmentEnumerator GetEnumerator()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.arraysegment-1.getenumerator?view=net-11.0) +#### Base64 + + * `OperationStatus DecodeFromChars(ReadOnlySpan, Span, int, int, bool)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.decodefromchars?view=net-11.0#system-buffers-text-base64-decodefromchars(system-readonlyspan((system-char))-system-span((system-byte))-system-int32@-system-int32@-system-boolean)) + * `int DecodeFromChars(ReadOnlySpan, Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.decodefromchars?view=net-11.0#system-buffers-text-base64-decodefromchars(system-readonlyspan((system-char))-system-span((system-byte)))) + * `byte[] DecodeFromChars(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.decodefromchars?view=net-11.0#system-buffers-text-base64-decodefromchars(system-readonlyspan((system-char)))) + * `byte[] DecodeFromUtf8(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.decodefromutf8?view=net-11.0#system-buffers-text-base64-decodefromutf8(system-readonlyspan((system-byte)))) + * `OperationStatus EncodeToChars(ReadOnlySpan, Span, int, int, bool)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetochars?view=net-11.0#system-buffers-text-base64-encodetochars(system-readonlyspan((system-byte))-system-span((system-char))-system-int32@-system-int32@-system-boolean)) + * `int EncodeToChars(ReadOnlySpan, Span)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetochars?view=net-11.0#system-buffers-text-base64-encodetochars(system-readonlyspan((system-byte))-system-span((system-char)))) + * `char[] EncodeToChars(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetochars?view=net-11.0#system-buffers-text-base64-encodetochars(system-readonlyspan((system-byte)))) + * `string EncodeToString(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetostring?view=net-11.0) + * `byte[] EncodeToUtf8(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetoutf8?view=net-11.0#system-buffers-text-base64-encodetoutf8(system-readonlyspan((system-byte)))) + * `int GetEncodedLength(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.getencodedlength?view=net-11.0) + * `int GetMaxDecodedLength(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.getmaxdecodedlength?view=net-11.0) + * `bool TryDecodeFromChars(ReadOnlySpan, Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.trydecodefromchars?view=net-11.0) + * `bool TryEncodeToChars(ReadOnlySpan, Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.tryencodetochars?view=net-11.0) + + #### BinaryPrimitives * `double ReadDoubleBigEndian(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.buffers.binary.binaryprimitives.readdoublebigendian?view=net-11.0) @@ -2170,7 +2187,7 @@ void ObjectDisposedExceptionExample(bool isDisposed) ObjectDisposedException.ThrowIf(isDisposed, typeof(Consume)); } ``` -snippet source | anchor +snippet source | anchor @@ -2189,7 +2206,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 efcf2680..c2d71864 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -258,7 +258,7 @@ void Base64UrlUsage() } #endif -#if FeatureMemory +#if FeatureMemory && !RefsBclMemory void Base64Usage() { ReadOnlySpan source = default; diff --git a/src/Polyfill/Base64Polyfill.cs b/src/Polyfill/Base64Polyfill.cs new file mode 100644 index 00000000..6fc1d669 --- /dev/null +++ b/src/Polyfill/Base64Polyfill.cs @@ -0,0 +1,221 @@ +#if FeatureMemory && !RefsBclMemory && !NET11_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Buffers; +using System.Buffers.Text; + +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.getencodedlength?view=net-11.0 + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + + return (bytesLength + 2) / 3 * 4; + } + + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.getmaxdecodedlength?view=net-11.0 + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + + return base64Length / 4 * 3; + } + + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetostring?view=net-11.0 + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetochars?view=net-11.0#system-buffers-text-base64-encodetochars(system-readonlyspan((system-byte))-system-span((system-char))) + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetochars?view=net-11.0#system-buffers-text-base64-encodetochars(system-readonlyspan((system-byte))) + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetochars?view=net-11.0#system-buffers-text-base64-encodetochars(system-readonlyspan((system-byte))-system-span((system-char))-system-int32@-system-int32@-system-boolean) + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + + return status; + } + + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.tryencodetochars?view=net-11.0 + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.encodetoutf8?view=net-11.0#system-buffers-text-base64-encodetoutf8(system-readonlyspan((system-byte))) + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + + return result; + } + + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.decodefromchars?view=net-11.0#system-buffers-text-base64-decodefromchars(system-readonlyspan((system-char))) + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.decodefromchars?view=net-11.0#system-buffers-text-base64-decodefromchars(system-readonlyspan((system-char))-system-span((system-byte))) + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + + decoded.CopyTo(destination); + return decoded.Length; + } + + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.decodefromchars?view=net-11.0#system-buffers-text-base64-decodefromchars(system-readonlyspan((system-char))-system-span((system-byte))-system-int32@-system-int32@-system-boolean) + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.trydecodefromchars?view=net-11.0 + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.buffers.text.base64.decodefromutf8?view=net-11.0#system-buffers-text-base64-decodefromutf8(system-readonlyspan((system-byte))) + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + + var chars = new char[source.Length]; + + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} + +#endif diff --git a/src/Polyfill/UriPolyfill.cs b/src/Polyfill/UriPolyfill.cs new file mode 100644 index 00000000..3802c397 --- /dev/null +++ b/src/Polyfill/UriPolyfill.cs @@ -0,0 +1,19 @@ +#if !NET11_0_OR_GREATER + +namespace Polyfills; + +using System; + +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uri.urischemedata?view=net-11.0 + public static string UriSchemeData => "data"; + } +} + +#endif diff --git a/src/Polyfill/Utf16.cs b/src/Polyfill/Utf16.cs new file mode 100644 index 00000000..b10538a4 --- /dev/null +++ b/src/Polyfill/Utf16.cs @@ -0,0 +1,66 @@ +#if FeatureMemory + +#if !NET11_0_OR_GREATER + +#pragma warning disable + +namespace System.Text.Unicode; + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.text.unicode.utf16.indexofinvalidsubsequence?view=net-11.0 + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + + // valid surrogate pair, skip the low surrogate + i++; + } + else if (char.IsLowSurrogate(c)) + { + // a low surrogate not preceded by a high surrogate + return i; + } + } + + return -1; + } + + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.text.unicode.utf16.isvalid?view=net-11.0 + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} + +#else +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Text.Unicode.Utf16))] +#endif + +#endif diff --git a/src/Polyfill/Utf8Polyfill.cs b/src/Polyfill/Utf8Polyfill.cs new file mode 100644 index 00000000..ee367361 --- /dev/null +++ b/src/Polyfill/Utf8Polyfill.cs @@ -0,0 +1,109 @@ +#if NETCOREAPP3_0_OR_GREATER && !NET11_0_OR_GREATER && !RefsBclMemory + +namespace Polyfills; + +using System; +using System.Text.Unicode; + +static partial class Polyfill +{ + extension(Utf8) + { + /// + /// Returns the index in where the first ill-formed UTF-8 subsequence begins, or -1 if is well-formed. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.text.unicode.utf8.indexofinvalidsubsequence?view=net-11.0 + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + // Validation follows the Unicode well-formed UTF-8 byte sequence table (Table 3-7). + var i = 0; + while (i < value.Length) + { + var b0 = value[i]; + + // single-byte ASCII + if (b0 <= 0x7F) + { + i++; + continue; + } + + int trailing; + byte lowerMin; + byte lowerMax; + if (b0 is >= 0xC2 and <= 0xDF) + { + trailing = 1; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xE0) + { + trailing = 2; + lowerMin = 0xA0; + lowerMax = 0xBF; + } + else if (b0 is >= 0xE1 and <= 0xEC or >= 0xEE and <= 0xEF) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xED) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0x9F; + } + else if (b0 == 0xF0) + { + trailing = 3; + lowerMin = 0x90; + lowerMax = 0xBF; + } + else if (b0 is >= 0xF1 and <= 0xF3) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xF4) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0x8F; + } + else + { + // invalid lead byte: 0x80-0xC1 (continuation/overlong) or 0xF5-0xFF + return i; + } + + // first continuation byte has a tighter valid range than the rest + if (i + 1 >= value.Length || + value[i + 1] < lowerMin || + value[i + 1] > lowerMax) + { + return i; + } + + // remaining continuation bytes must each be 0x80-0xBF + for (var j = 2; j <= trailing; j++) + { + if (i + j >= value.Length || + value[i + j] < 0x80 || + value[i + j] > 0xBF) + { + return i; + } + } + + i += trailing + 1; + } + + return -1; + } + } +} + +#endif diff --git a/src/Split/net10.0/Base64Polyfill.cs b/src/Split/net10.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net10.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net10.0/UriPolyfill.cs b/src/Split/net10.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net10.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net10.0/Utf16.cs b/src/Split/net10.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net10.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net10.0/Utf8Polyfill.cs b/src/Split/net10.0/Utf8Polyfill.cs new file mode 100644 index 00000000..d094d78d --- /dev/null +++ b/src/Split/net10.0/Utf8Polyfill.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable +#if !RefsBclMemory +namespace Polyfills; +using System; +using System.Text.Unicode; +static partial class Polyfill +{ + extension(Utf8) + { + /// + /// Returns the index in where the first ill-formed UTF-8 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + var i = 0; + while (i < value.Length) + { + var b0 = value[i]; + if (b0 <= 0x7F) + { + i++; + continue; + } + int trailing; + byte lowerMin; + byte lowerMax; + if (b0 is >= 0xC2 and <= 0xDF) + { + trailing = 1; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xE0) + { + trailing = 2; + lowerMin = 0xA0; + lowerMax = 0xBF; + } + else if (b0 is >= 0xE1 and <= 0xEC or >= 0xEE and <= 0xEF) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xED) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0x9F; + } + else if (b0 == 0xF0) + { + trailing = 3; + lowerMin = 0x90; + lowerMax = 0xBF; + } + else if (b0 is >= 0xF1 and <= 0xF3) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xF4) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0x8F; + } + else + { + return i; + } + if (i + 1 >= value.Length || + value[i + 1] < lowerMin || + value[i + 1] > lowerMax) + { + return i; + } + for (var j = 2; j <= trailing; j++) + { + if (i + j >= value.Length || + value[i + j] < 0x80 || + value[i + j] > 0xBF) + { + return i; + } + } + i += trailing + 1; + } + return -1; + } + } +} +#endif diff --git a/src/Split/net461/Base64Polyfill.cs b/src/Split/net461/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net461/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net461/UriPolyfill.cs b/src/Split/net461/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net461/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net461/Utf16.cs b/src/Split/net461/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net461/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net462/Base64Polyfill.cs b/src/Split/net462/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net462/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net462/UriPolyfill.cs b/src/Split/net462/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net462/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net462/Utf16.cs b/src/Split/net462/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net462/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net47/Base64Polyfill.cs b/src/Split/net47/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net47/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net47/UriPolyfill.cs b/src/Split/net47/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net47/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net47/Utf16.cs b/src/Split/net47/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net47/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net471/Base64Polyfill.cs b/src/Split/net471/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net471/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net471/UriPolyfill.cs b/src/Split/net471/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net471/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net471/Utf16.cs b/src/Split/net471/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net471/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net472/Base64Polyfill.cs b/src/Split/net472/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net472/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net472/UriPolyfill.cs b/src/Split/net472/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net472/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net472/Utf16.cs b/src/Split/net472/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net472/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net48/Base64Polyfill.cs b/src/Split/net48/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net48/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net48/UriPolyfill.cs b/src/Split/net48/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net48/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net48/Utf16.cs b/src/Split/net48/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net48/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net481/Base64Polyfill.cs b/src/Split/net481/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net481/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net481/UriPolyfill.cs b/src/Split/net481/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net481/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net481/Utf16.cs b/src/Split/net481/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net481/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net5.0/Base64Polyfill.cs b/src/Split/net5.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net5.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net5.0/UriPolyfill.cs b/src/Split/net5.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net5.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net5.0/Utf16.cs b/src/Split/net5.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net5.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net5.0/Utf8Polyfill.cs b/src/Split/net5.0/Utf8Polyfill.cs new file mode 100644 index 00000000..d094d78d --- /dev/null +++ b/src/Split/net5.0/Utf8Polyfill.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable +#if !RefsBclMemory +namespace Polyfills; +using System; +using System.Text.Unicode; +static partial class Polyfill +{ + extension(Utf8) + { + /// + /// Returns the index in where the first ill-formed UTF-8 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + var i = 0; + while (i < value.Length) + { + var b0 = value[i]; + if (b0 <= 0x7F) + { + i++; + continue; + } + int trailing; + byte lowerMin; + byte lowerMax; + if (b0 is >= 0xC2 and <= 0xDF) + { + trailing = 1; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xE0) + { + trailing = 2; + lowerMin = 0xA0; + lowerMax = 0xBF; + } + else if (b0 is >= 0xE1 and <= 0xEC or >= 0xEE and <= 0xEF) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xED) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0x9F; + } + else if (b0 == 0xF0) + { + trailing = 3; + lowerMin = 0x90; + lowerMax = 0xBF; + } + else if (b0 is >= 0xF1 and <= 0xF3) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xF4) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0x8F; + } + else + { + return i; + } + if (i + 1 >= value.Length || + value[i + 1] < lowerMin || + value[i + 1] > lowerMax) + { + return i; + } + for (var j = 2; j <= trailing; j++) + { + if (i + j >= value.Length || + value[i + j] < 0x80 || + value[i + j] > 0xBF) + { + return i; + } + } + i += trailing + 1; + } + return -1; + } + } +} +#endif diff --git a/src/Split/net6.0/Base64Polyfill.cs b/src/Split/net6.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net6.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net6.0/UriPolyfill.cs b/src/Split/net6.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net6.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net6.0/Utf16.cs b/src/Split/net6.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net6.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net6.0/Utf8Polyfill.cs b/src/Split/net6.0/Utf8Polyfill.cs new file mode 100644 index 00000000..d094d78d --- /dev/null +++ b/src/Split/net6.0/Utf8Polyfill.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable +#if !RefsBclMemory +namespace Polyfills; +using System; +using System.Text.Unicode; +static partial class Polyfill +{ + extension(Utf8) + { + /// + /// Returns the index in where the first ill-formed UTF-8 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + var i = 0; + while (i < value.Length) + { + var b0 = value[i]; + if (b0 <= 0x7F) + { + i++; + continue; + } + int trailing; + byte lowerMin; + byte lowerMax; + if (b0 is >= 0xC2 and <= 0xDF) + { + trailing = 1; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xE0) + { + trailing = 2; + lowerMin = 0xA0; + lowerMax = 0xBF; + } + else if (b0 is >= 0xE1 and <= 0xEC or >= 0xEE and <= 0xEF) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xED) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0x9F; + } + else if (b0 == 0xF0) + { + trailing = 3; + lowerMin = 0x90; + lowerMax = 0xBF; + } + else if (b0 is >= 0xF1 and <= 0xF3) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xF4) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0x8F; + } + else + { + return i; + } + if (i + 1 >= value.Length || + value[i + 1] < lowerMin || + value[i + 1] > lowerMax) + { + return i; + } + for (var j = 2; j <= trailing; j++) + { + if (i + j >= value.Length || + value[i + j] < 0x80 || + value[i + j] > 0xBF) + { + return i; + } + } + i += trailing + 1; + } + return -1; + } + } +} +#endif diff --git a/src/Split/net7.0/Base64Polyfill.cs b/src/Split/net7.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net7.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net7.0/UriPolyfill.cs b/src/Split/net7.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net7.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net7.0/Utf16.cs b/src/Split/net7.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net7.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net7.0/Utf8Polyfill.cs b/src/Split/net7.0/Utf8Polyfill.cs new file mode 100644 index 00000000..d094d78d --- /dev/null +++ b/src/Split/net7.0/Utf8Polyfill.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable +#if !RefsBclMemory +namespace Polyfills; +using System; +using System.Text.Unicode; +static partial class Polyfill +{ + extension(Utf8) + { + /// + /// Returns the index in where the first ill-formed UTF-8 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + var i = 0; + while (i < value.Length) + { + var b0 = value[i]; + if (b0 <= 0x7F) + { + i++; + continue; + } + int trailing; + byte lowerMin; + byte lowerMax; + if (b0 is >= 0xC2 and <= 0xDF) + { + trailing = 1; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xE0) + { + trailing = 2; + lowerMin = 0xA0; + lowerMax = 0xBF; + } + else if (b0 is >= 0xE1 and <= 0xEC or >= 0xEE and <= 0xEF) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xED) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0x9F; + } + else if (b0 == 0xF0) + { + trailing = 3; + lowerMin = 0x90; + lowerMax = 0xBF; + } + else if (b0 is >= 0xF1 and <= 0xF3) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xF4) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0x8F; + } + else + { + return i; + } + if (i + 1 >= value.Length || + value[i + 1] < lowerMin || + value[i + 1] > lowerMax) + { + return i; + } + for (var j = 2; j <= trailing; j++) + { + if (i + j >= value.Length || + value[i + j] < 0x80 || + value[i + j] > 0xBF) + { + return i; + } + } + i += trailing + 1; + } + return -1; + } + } +} +#endif diff --git a/src/Split/net8.0/Base64Polyfill.cs b/src/Split/net8.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net8.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net8.0/UriPolyfill.cs b/src/Split/net8.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net8.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net8.0/Utf16.cs b/src/Split/net8.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net8.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net8.0/Utf8Polyfill.cs b/src/Split/net8.0/Utf8Polyfill.cs new file mode 100644 index 00000000..d094d78d --- /dev/null +++ b/src/Split/net8.0/Utf8Polyfill.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable +#if !RefsBclMemory +namespace Polyfills; +using System; +using System.Text.Unicode; +static partial class Polyfill +{ + extension(Utf8) + { + /// + /// Returns the index in where the first ill-formed UTF-8 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + var i = 0; + while (i < value.Length) + { + var b0 = value[i]; + if (b0 <= 0x7F) + { + i++; + continue; + } + int trailing; + byte lowerMin; + byte lowerMax; + if (b0 is >= 0xC2 and <= 0xDF) + { + trailing = 1; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xE0) + { + trailing = 2; + lowerMin = 0xA0; + lowerMax = 0xBF; + } + else if (b0 is >= 0xE1 and <= 0xEC or >= 0xEE and <= 0xEF) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xED) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0x9F; + } + else if (b0 == 0xF0) + { + trailing = 3; + lowerMin = 0x90; + lowerMax = 0xBF; + } + else if (b0 is >= 0xF1 and <= 0xF3) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xF4) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0x8F; + } + else + { + return i; + } + if (i + 1 >= value.Length || + value[i + 1] < lowerMin || + value[i + 1] > lowerMax) + { + return i; + } + for (var j = 2; j <= trailing; j++) + { + if (i + j >= value.Length || + value[i + j] < 0x80 || + value[i + j] > 0xBF) + { + return i; + } + } + i += trailing + 1; + } + return -1; + } + } +} +#endif diff --git a/src/Split/net9.0/Base64Polyfill.cs b/src/Split/net9.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/net9.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/net9.0/UriPolyfill.cs b/src/Split/net9.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/net9.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/net9.0/Utf16.cs b/src/Split/net9.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/net9.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/net9.0/Utf8Polyfill.cs b/src/Split/net9.0/Utf8Polyfill.cs new file mode 100644 index 00000000..d094d78d --- /dev/null +++ b/src/Split/net9.0/Utf8Polyfill.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable +#if !RefsBclMemory +namespace Polyfills; +using System; +using System.Text.Unicode; +static partial class Polyfill +{ + extension(Utf8) + { + /// + /// Returns the index in where the first ill-formed UTF-8 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + var i = 0; + while (i < value.Length) + { + var b0 = value[i]; + if (b0 <= 0x7F) + { + i++; + continue; + } + int trailing; + byte lowerMin; + byte lowerMax; + if (b0 is >= 0xC2 and <= 0xDF) + { + trailing = 1; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xE0) + { + trailing = 2; + lowerMin = 0xA0; + lowerMax = 0xBF; + } + else if (b0 is >= 0xE1 and <= 0xEC or >= 0xEE and <= 0xEF) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xED) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0x9F; + } + else if (b0 == 0xF0) + { + trailing = 3; + lowerMin = 0x90; + lowerMax = 0xBF; + } + else if (b0 is >= 0xF1 and <= 0xF3) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xF4) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0x8F; + } + else + { + return i; + } + if (i + 1 >= value.Length || + value[i + 1] < lowerMin || + value[i + 1] > lowerMax) + { + return i; + } + for (var j = 2; j <= trailing; j++) + { + if (i + j >= value.Length || + value[i + j] < 0x80 || + value[i + j] > 0xBF) + { + return i; + } + } + i += trailing + 1; + } + return -1; + } + } +} +#endif diff --git a/src/Split/netcoreapp2.0/Base64Polyfill.cs b/src/Split/netcoreapp2.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/netcoreapp2.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/netcoreapp2.0/UriPolyfill.cs b/src/Split/netcoreapp2.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/netcoreapp2.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/netcoreapp2.0/Utf16.cs b/src/Split/netcoreapp2.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/netcoreapp2.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/netcoreapp2.1/Base64Polyfill.cs b/src/Split/netcoreapp2.1/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/netcoreapp2.1/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/netcoreapp2.1/UriPolyfill.cs b/src/Split/netcoreapp2.1/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/netcoreapp2.1/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/netcoreapp2.1/Utf16.cs b/src/Split/netcoreapp2.1/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/netcoreapp2.1/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/netcoreapp2.2/Base64Polyfill.cs b/src/Split/netcoreapp2.2/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/netcoreapp2.2/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/netcoreapp2.2/UriPolyfill.cs b/src/Split/netcoreapp2.2/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/netcoreapp2.2/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/netcoreapp2.2/Utf16.cs b/src/Split/netcoreapp2.2/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/netcoreapp2.2/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/netcoreapp3.0/Base64Polyfill.cs b/src/Split/netcoreapp3.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/netcoreapp3.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/netcoreapp3.0/UriPolyfill.cs b/src/Split/netcoreapp3.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/netcoreapp3.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/netcoreapp3.0/Utf16.cs b/src/Split/netcoreapp3.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/netcoreapp3.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/netcoreapp3.0/Utf8Polyfill.cs b/src/Split/netcoreapp3.0/Utf8Polyfill.cs new file mode 100644 index 00000000..d094d78d --- /dev/null +++ b/src/Split/netcoreapp3.0/Utf8Polyfill.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable +#if !RefsBclMemory +namespace Polyfills; +using System; +using System.Text.Unicode; +static partial class Polyfill +{ + extension(Utf8) + { + /// + /// Returns the index in where the first ill-formed UTF-8 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + var i = 0; + while (i < value.Length) + { + var b0 = value[i]; + if (b0 <= 0x7F) + { + i++; + continue; + } + int trailing; + byte lowerMin; + byte lowerMax; + if (b0 is >= 0xC2 and <= 0xDF) + { + trailing = 1; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xE0) + { + trailing = 2; + lowerMin = 0xA0; + lowerMax = 0xBF; + } + else if (b0 is >= 0xE1 and <= 0xEC or >= 0xEE and <= 0xEF) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xED) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0x9F; + } + else if (b0 == 0xF0) + { + trailing = 3; + lowerMin = 0x90; + lowerMax = 0xBF; + } + else if (b0 is >= 0xF1 and <= 0xF3) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xF4) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0x8F; + } + else + { + return i; + } + if (i + 1 >= value.Length || + value[i + 1] < lowerMin || + value[i + 1] > lowerMax) + { + return i; + } + for (var j = 2; j <= trailing; j++) + { + if (i + j >= value.Length || + value[i + j] < 0x80 || + value[i + j] > 0xBF) + { + return i; + } + } + i += trailing + 1; + } + return -1; + } + } +} +#endif diff --git a/src/Split/netcoreapp3.1/Base64Polyfill.cs b/src/Split/netcoreapp3.1/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/netcoreapp3.1/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/netcoreapp3.1/UriPolyfill.cs b/src/Split/netcoreapp3.1/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/netcoreapp3.1/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/netcoreapp3.1/Utf16.cs b/src/Split/netcoreapp3.1/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/netcoreapp3.1/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/netcoreapp3.1/Utf8Polyfill.cs b/src/Split/netcoreapp3.1/Utf8Polyfill.cs new file mode 100644 index 00000000..d094d78d --- /dev/null +++ b/src/Split/netcoreapp3.1/Utf8Polyfill.cs @@ -0,0 +1,95 @@ +// +#pragma warning disable +#if !RefsBclMemory +namespace Polyfills; +using System; +using System.Text.Unicode; +static partial class Polyfill +{ + extension(Utf8) + { + /// + /// Returns the index in where the first ill-formed UTF-8 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + var i = 0; + while (i < value.Length) + { + var b0 = value[i]; + if (b0 <= 0x7F) + { + i++; + continue; + } + int trailing; + byte lowerMin; + byte lowerMax; + if (b0 is >= 0xC2 and <= 0xDF) + { + trailing = 1; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xE0) + { + trailing = 2; + lowerMin = 0xA0; + lowerMax = 0xBF; + } + else if (b0 is >= 0xE1 and <= 0xEC or >= 0xEE and <= 0xEF) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xED) + { + trailing = 2; + lowerMin = 0x80; + lowerMax = 0x9F; + } + else if (b0 == 0xF0) + { + trailing = 3; + lowerMin = 0x90; + lowerMax = 0xBF; + } + else if (b0 is >= 0xF1 and <= 0xF3) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0xBF; + } + else if (b0 == 0xF4) + { + trailing = 3; + lowerMin = 0x80; + lowerMax = 0x8F; + } + else + { + return i; + } + if (i + 1 >= value.Length || + value[i + 1] < lowerMin || + value[i + 1] > lowerMax) + { + return i; + } + for (var j = 2; j <= trailing; j++) + { + if (i + j >= value.Length || + value[i + j] < 0x80 || + value[i + j] > 0xBF) + { + return i; + } + } + i += trailing + 1; + } + return -1; + } + } +} +#endif diff --git a/src/Split/netstandard2.0/Base64Polyfill.cs b/src/Split/netstandard2.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/netstandard2.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/netstandard2.0/UriPolyfill.cs b/src/Split/netstandard2.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/netstandard2.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/netstandard2.0/Utf16.cs b/src/Split/netstandard2.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/netstandard2.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/netstandard2.1/Base64Polyfill.cs b/src/Split/netstandard2.1/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/netstandard2.1/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/netstandard2.1/UriPolyfill.cs b/src/Split/netstandard2.1/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/netstandard2.1/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/netstandard2.1/Utf16.cs b/src/Split/netstandard2.1/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/netstandard2.1/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Split/uap10.0/Base64Polyfill.cs b/src/Split/uap10.0/Base64Polyfill.cs new file mode 100644 index 00000000..c2d7dfb5 --- /dev/null +++ b/src/Split/uap10.0/Base64Polyfill.cs @@ -0,0 +1,173 @@ +// +#pragma warning disable +#if FeatureMemory && !RefsBclMemory +namespace Polyfills; +using System; +using System.Buffers; +using System.Buffers.Text; +static partial class Polyfill +{ + extension(Base64) + { + /// + /// Returns the length (in chars) of the result if you were to encode binary data within a byte span of size . + /// + public static int GetEncodedLength(int bytesLength) + { + if (bytesLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(bytesLength)); + } + return (bytesLength + 2) / 3 * 4; + } + /// + /// Returns the maximum length (in bytes) of the result if you were to decode base-64 encoded text of size . + /// + public static int GetMaxDecodedLength(int base64Length) + { + if (base64Length < 0) + { + throw new ArgumentOutOfRangeException(nameof(base64Length)); + } + return base64Length / 4 * 3; + } + /// + /// Encodes the span of binary data into a string that is represented as base-64. + /// + public static string EncodeToString(ReadOnlySpan source) => + EncodeBase64(source); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static int EncodeToChars(ReadOnlySpan source, Span destination) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + encoded.AsSpan().CopyTo(destination); + return encoded.Length; + } + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static char[] EncodeToChars(ReadOnlySpan source) => + EncodeBase64(source).ToCharArray(); + /// + /// Encodes the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static OperationStatus EncodeToChars(ReadOnlySpan source, Span destination, out int bytesConsumed, out int charsWritten, bool isFinalBlock = true) + { + var utf8 = new byte[destination.Length]; + var status = Base64.EncodeToUtf8(source, utf8, out bytesConsumed, out charsWritten, isFinalBlock); + for (var i = 0; i < charsWritten; i++) + { + destination[i] = (char) utf8[i]; + } + return status; + } + /// + /// Tries to encode the span of binary data into UTF-16 encoded text represented as base-64. + /// + public static bool TryEncodeToChars(ReadOnlySpan source, Span destination, out int charsWritten) + { + var encoded = EncodeBase64(source); + if (encoded.Length > destination.Length) + { + charsWritten = 0; + return false; + } + encoded.AsSpan().CopyTo(destination); + charsWritten = encoded.Length; + return true; + } + /// + /// Encodes the span of binary data into a byte array of UTF-8 encoded text represented as base-64. + /// + public static byte[] EncodeToUtf8(ReadOnlySpan source) + { + var encoded = EncodeBase64(source); + var result = new byte[encoded.Length]; + for (var i = 0; i < encoded.Length; i++) + { + result[i] = (byte) encoded[i]; + } + return result; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromChars(ReadOnlySpan source) => + DecodeBase64(source); + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static int DecodeFromChars(ReadOnlySpan source, Span destination) + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + throw new ArgumentException("Destination is too small.", nameof(destination)); + } + decoded.CopyTo(destination); + return decoded.Length; + } + /// + /// Decodes the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static OperationStatus DecodeFromChars(ReadOnlySpan source, Span destination, out int charsConsumed, out int bytesWritten, bool isFinalBlock = true) + { + var utf8 = new byte[source.Length]; + for (var i = 0; i < source.Length; i++) + { + utf8[i] = (byte) source[i]; + } + return Base64.DecodeFromUtf8(utf8, destination, out charsConsumed, out bytesWritten, isFinalBlock); + } + /// + /// Tries to decode the span of UTF-16 encoded text represented as base-64 into binary data. + /// + public static bool TryDecodeFromChars(ReadOnlySpan source, Span destination, out int bytesWritten) + { + try + { + var decoded = DecodeBase64(source); + if (decoded.Length > destination.Length) + { + bytesWritten = 0; + return false; + } + decoded.CopyTo(destination); + bytesWritten = decoded.Length; + return true; + } + catch (FormatException) + { + bytesWritten = 0; + return false; + } + } + /// + /// Decodes the span of UTF-8 encoded text represented as base-64 into binary data. + /// + public static byte[] DecodeFromUtf8(ReadOnlySpan source) + { + if (source.IsEmpty) + { + return Array.Empty(); + } + var chars = new char[source.Length]; + for (var i = 0; i < source.Length; i++) + { + chars[i] = (char) source[i]; + } + return Convert.FromBase64CharArray(chars, 0, chars.Length); + } + static string EncodeBase64(ReadOnlySpan source) => + source.IsEmpty ? string.Empty : Convert.ToBase64String(source.ToArray()); + static byte[] DecodeBase64(ReadOnlySpan source) => + source.IsEmpty ? Array.Empty() : Convert.FromBase64CharArray(source.ToArray(), 0, source.Length); + } +} +#endif diff --git a/src/Split/uap10.0/UriPolyfill.cs b/src/Split/uap10.0/UriPolyfill.cs new file mode 100644 index 00000000..81679bd1 --- /dev/null +++ b/src/Split/uap10.0/UriPolyfill.cs @@ -0,0 +1,14 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +static partial class Polyfill +{ + extension(Uri) + { + /// + /// Provides the scheme name for the data URI scheme (RFC 2397). This field is read-only. + /// + public static string UriSchemeData => "data"; + } +} diff --git a/src/Split/uap10.0/Utf16.cs b/src/Split/uap10.0/Utf16.cs new file mode 100644 index 00000000..30b97bf2 --- /dev/null +++ b/src/Split/uap10.0/Utf16.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable +#if FeatureMemory +#pragma warning disable +namespace System.Text.Unicode; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +/// Provides static methods for validating UTF-16 text. +[ExcludeFromCodeCoverage] +[DebuggerNonUserCode] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +static class Utf16 +{ + /// + /// Returns the index in where the first ill-formed UTF-16 subsequence begins, or -1 if is well-formed. + /// + public static int IndexOfInvalidSubsequence(ReadOnlySpan value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (char.IsHighSurrogate(c)) + { + if (i + 1 >= value.Length || + !char.IsLowSurrogate(value[i + 1])) + { + return i; + } + i++; + } + else if (char.IsLowSurrogate(c)) + { + return i; + } + } + return -1; + } + /// + /// Returns a value indicating whether is well-formed UTF-16. + /// + public static bool IsValid(ReadOnlySpan value) => + IndexOfInvalidSubsequence(value) < 0; +} +#endif diff --git a/src/Tests/Base64Tests.cs b/src/Tests/Base64Tests.cs new file mode 100644 index 00000000..5cc2016c --- /dev/null +++ b/src/Tests/Base64Tests.cs @@ -0,0 +1,161 @@ +#if FeatureMemory + +using System.Buffers; +using System.Buffers.Text; + +public class Base64Tests +{ + // "Hello" -> standard base64 "SGVsbG8=" + static readonly byte[] HelloBytes = "Hello"u8.ToArray(); + const string HelloEncoded = "SGVsbG8="; + + [Test] + public async Task GetEncodedLength() + { + await Assert.That(Base64.GetEncodedLength(0)).IsEqualTo(0); + await Assert.That(Base64.GetEncodedLength(1)).IsEqualTo(4); + await Assert.That(Base64.GetEncodedLength(2)).IsEqualTo(4); + await Assert.That(Base64.GetEncodedLength(3)).IsEqualTo(4); + await Assert.That(Base64.GetEncodedLength(5)).IsEqualTo(8); + } + + [Test] + public async Task GetMaxDecodedLength() + { + await Assert.That(Base64.GetMaxDecodedLength(0)).IsEqualTo(0); + await Assert.That(Base64.GetMaxDecodedLength(4)).IsEqualTo(3); + await Assert.That(Base64.GetMaxDecodedLength(8)).IsEqualTo(6); + } + + [Test] + public async Task EncodeToString() + { + var result = Base64.EncodeToString(HelloBytes); + await Assert.That(result).IsEqualTo(HelloEncoded); + } + + [Test] + public async Task EncodeToString_Empty() + { + var result = Base64.EncodeToString(ReadOnlySpan.Empty); + await Assert.That(result).IsEqualTo(string.Empty); + } + + [Test] + public async Task EncodeToChars_Span() + { + var destination = new char[Base64.GetEncodedLength(HelloBytes.Length)]; + var written = Base64.EncodeToChars(HelloBytes, destination); + var result = new string(destination, 0, written); + await Assert.That(result).IsEqualTo(HelloEncoded); + } + + [Test] + public async Task EncodeToChars_Array() + { + var chars = Base64.EncodeToChars(HelloBytes); + var result = new string(chars); + await Assert.That(result).IsEqualTo(HelloEncoded); + } + + [Test] + public async Task EncodeToChars_OperationStatus() + { + var destination = new char[Base64.GetEncodedLength(HelloBytes.Length)]; + var status = Base64.EncodeToChars(HelloBytes, destination, out var consumed, out var written, isFinalBlock: true); + var result = new string(destination, 0, written); + await Assert.That(status).IsEqualTo(OperationStatus.Done); + await Assert.That(consumed).IsEqualTo(HelloBytes.Length); + await Assert.That(result).IsEqualTo(HelloEncoded); + } + + [Test] + public async Task TryEncodeToChars_Success() + { + var destination = new char[20]; + var success = Base64.TryEncodeToChars(HelloBytes, destination, out var written); + var result = new string(destination, 0, written); + await Assert.That(success).IsTrue(); + await Assert.That(result).IsEqualTo(HelloEncoded); + } + + [Test] + public async Task TryEncodeToChars_TooSmall() + { + var destination = new char[2]; + var success = Base64.TryEncodeToChars(HelloBytes, destination, out var written); + await Assert.That(success).IsFalse(); + await Assert.That(written).IsEqualTo(0); + } + + [Test] + public async Task EncodeToUtf8_Array() + { + var result = Base64.EncodeToUtf8(HelloBytes); + var expected = System.Text.Encoding.UTF8.GetBytes(HelloEncoded); + await Assert.That(result).IsEquivalentTo(expected); + } + + [Test] + public async Task DecodeFromChars_Array() + { + var result = Base64.DecodeFromChars(HelloEncoded.AsSpan()); + await Assert.That(result).IsEquivalentTo(HelloBytes); + } + + [Test] + public async Task DecodeFromChars_Span() + { + var destination = new byte[Base64.GetMaxDecodedLength(HelloEncoded.Length)]; + var written = Base64.DecodeFromChars(HelloEncoded.AsSpan(), destination); + var decoded = destination.AsSpan(0, written).ToArray(); + await Assert.That(decoded).IsEquivalentTo(HelloBytes); + } + + [Test] + public async Task DecodeFromChars_OperationStatus() + { + var destination = new byte[Base64.GetMaxDecodedLength(HelloEncoded.Length)]; + var status = Base64.DecodeFromChars(HelloEncoded.AsSpan(), destination, out var consumed, out var written, isFinalBlock: true); + var decoded = destination.AsSpan(0, written).ToArray(); + await Assert.That(status).IsEqualTo(OperationStatus.Done); + await Assert.That(consumed).IsEqualTo(HelloEncoded.Length); + await Assert.That(decoded).IsEquivalentTo(HelloBytes); + } + + [Test] + public async Task TryDecodeFromChars_Success() + { + var destination = new byte[Base64.GetMaxDecodedLength(HelloEncoded.Length)]; + var success = Base64.TryDecodeFromChars(HelloEncoded.AsSpan(), destination, out var written); + var decoded = destination.AsSpan(0, written).ToArray(); + await Assert.That(success).IsTrue(); + await Assert.That(decoded).IsEquivalentTo(HelloBytes); + } + + [Test] + public async Task DecodeFromUtf8_Array() + { + var utf8 = "SGVsbG8="u8.ToArray(); + var result = Base64.DecodeFromUtf8(utf8); + await Assert.That(result).IsEquivalentTo(HelloBytes); + } + + [Test] + public async Task DecodeFromChars_Empty() + { + var result = Base64.DecodeFromChars(ReadOnlySpan.Empty); + await Assert.That(result).IsEmpty(); + } + + [Test] + public async Task RoundTrip() + { + byte[] data = [0x00, 0x01, 0x02, 0xFB, 0xFF, 0xFE, 0x10]; + var encoded = Base64.EncodeToString(data); + var decoded = Base64.DecodeFromChars(encoded.AsSpan()); + await Assert.That(decoded).IsEquivalentTo(data); + } +} + +#endif diff --git a/src/Tests/UriTests.cs b/src/Tests/UriTests.cs new file mode 100644 index 00000000..d72e096f --- /dev/null +++ b/src/Tests/UriTests.cs @@ -0,0 +1,11 @@ +namespace Tests; + +public class UriTests +{ + [Test] + public async Task UriSchemeData() + { + var scheme = Uri.UriSchemeData; + await Assert.That(scheme).IsEqualTo("data"); + } +} diff --git a/src/Tests/Utf16Tests.cs b/src/Tests/Utf16Tests.cs new file mode 100644 index 00000000..ad9acd00 --- /dev/null +++ b/src/Tests/Utf16Tests.cs @@ -0,0 +1,58 @@ +#if FeatureMemory + +using System.Text.Unicode; + +public class Utf16Tests +{ + [Test] + public async Task IsValid_Ascii() + { + var result = Utf16.IsValid("Hello".AsSpan()); + await Assert.That(result).IsTrue(); + } + + [Test] + public async Task IsValid_ValidSurrogatePair() + { + // U+1F44D (👍) encoded as a high/low surrogate pair + var result = Utf16.IsValid("👍".AsSpan()); + await Assert.That(result).IsTrue(); + } + + [Test] + public async Task IsValid_UnpairedHighSurrogate() + { + var result = Utf16.IsValid("\uD83D".AsSpan()); + await Assert.That(result).IsFalse(); + } + + [Test] + public async Task IsValid_UnpairedLowSurrogate() + { + var result = Utf16.IsValid("a\uDC4D".AsSpan()); + await Assert.That(result).IsFalse(); + } + + [Test] + public async Task IndexOfInvalidSubsequence_Valid() + { + var index = Utf16.IndexOfInvalidSubsequence("ab👍cd".AsSpan()); + await Assert.That(index).IsEqualTo(-1); + } + + [Test] + public async Task IndexOfInvalidSubsequence_HighSurrogateAtEnd() + { + var index = Utf16.IndexOfInvalidSubsequence("ab\uD83D".AsSpan()); + await Assert.That(index).IsEqualTo(2); + } + + [Test] + public async Task IndexOfInvalidSubsequence_LoneLowSurrogate() + { + var index = Utf16.IndexOfInvalidSubsequence("a\uDC4Db".AsSpan()); + await Assert.That(index).IsEqualTo(1); + } +} + +#endif diff --git a/src/Tests/Utf8Tests.cs b/src/Tests/Utf8Tests.cs new file mode 100644 index 00000000..d0e82ced --- /dev/null +++ b/src/Tests/Utf8Tests.cs @@ -0,0 +1,60 @@ +#if NETCOREAPP3_0_OR_GREATER + +using System.Text.Unicode; + +public class Utf8Tests +{ + [Test] + public async Task IndexOfInvalidSubsequence_Valid() + { + byte[] bytes = "Hello"u8.ToArray(); + var index = Utf8.IndexOfInvalidSubsequence(bytes); + await Assert.That(index).IsEqualTo(-1); + } + + [Test] + public async Task IndexOfInvalidSubsequence_ValidMultibyte() + { + // U+1F44D 👍 = F0 9F 91 8D + byte[] bytes = [0xF0, 0x9F, 0x91, 0x8D]; + var index = Utf8.IndexOfInvalidSubsequence(bytes); + await Assert.That(index).IsEqualTo(-1); + } + + [Test] + public async Task IndexOfInvalidSubsequence_BadContinuation() + { + // 0xC3 starts a 2-byte sequence but 0x28 is not a continuation byte + byte[] bytes = [0xC3, 0x28]; + var index = Utf8.IndexOfInvalidSubsequence(bytes); + await Assert.That(index).IsEqualTo(0); + } + + [Test] + public async Task IndexOfInvalidSubsequence_LoneContinuation() + { + byte[] bytes = [0x41, 0x80, 0x42]; + var index = Utf8.IndexOfInvalidSubsequence(bytes); + await Assert.That(index).IsEqualTo(1); + } + + [Test] + public async Task IndexOfInvalidSubsequence_Truncated() + { + // 'A' followed by an incomplete 4-byte sequence (F0 9F 91 then EOF) + byte[] bytes = [0x41, 0xF0, 0x9F, 0x91]; + var index = Utf8.IndexOfInvalidSubsequence(bytes); + await Assert.That(index).IsEqualTo(1); + } + + [Test] + public async Task IndexOfInvalidSubsequence_Surrogate() + { + // ED A0 80 would encode U+D800 (a surrogate) and is ill-formed + byte[] bytes = [0xED, 0xA0, 0x80]; + var index = Utf8.IndexOfInvalidSubsequence(bytes); + await Assert.That(index).IsEqualTo(0); + } +} + +#endif From f7d02821c6cc753a4480976199e997b1035965f6 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 10 Jun 2026 14:16:54 +1000 Subject: [PATCH 2/2] Update Directory.Build.props --- src/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index bdac1091..6e649b9f 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ CS1591;NETSDK1138;NU1901;NU1902;NU1903;CA1822;CA1847;CA1861;NU1510;NU1608;NU1109 - 10.8.1 + 10.9.0 1.0.0 Polyfill true