diff --git a/apiCount.include.md b/apiCount.include.md
index 41ee45b3..876081b7 100644
--- a/apiCount.include.md
+++ b/apiCount.include.md
@@ -4,25 +4,25 @@
| Target | APIs |
| -- | -- |
-| `net461` | 972 |
-| `net462` | 972 |
-| `net47` | 971 |
-| `net471` | 970 |
-| `net472` | 966 |
-| `net48` | 966 |
-| `net481` | 966 |
-| `netstandard2.0` | 968 |
-| `netstandard2.1` | 821 |
-| `netcoreapp2.0` | 891 |
-| `netcoreapp2.1` | 832 |
-| `netcoreapp2.2` | 832 |
-| `netcoreapp3.0` | 779 |
-| `netcoreapp3.1` | 778 |
-| `net5.0` | 650 |
-| `net6.0` | 555 |
+| `net461` | 973 |
+| `net462` | 973 |
+| `net47` | 972 |
+| `net471` | 971 |
+| `net472` | 967 |
+| `net48` | 967 |
+| `net481` | 967 |
+| `netstandard2.0` | 969 |
+| `netstandard2.1` | 822 |
+| `netcoreapp2.0` | 892 |
+| `netcoreapp2.1` | 833 |
+| `netcoreapp2.2` | 833 |
+| `netcoreapp3.0` | 785 |
+| `netcoreapp3.1` | 784 |
+| `net5.0` | 656 |
+| `net6.0` | 557 |
| `net7.0` | 402 |
| `net8.0` | 283 |
| `net9.0` | 189 |
| `net10.0` | 135 |
| `net11.0` | 58 |
-| `uap10.0` | 958 |
+| `uap10.0` | 959 |
diff --git a/assemblySize.include.md b/assemblySize.include.md
index dd4f2db9..18f13d19 100644
--- a/assemblySize.include.md
+++ b/assemblySize.include.md
@@ -2,51 +2,51 @@
| | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability |
|----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------|
-| netstandard2.0 | 8.0KB | 358.5KB | +350.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB |
-| netstandard2.1 | 8.5KB | 312.5KB | +304.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net461 | 8.5KB | 357.0KB | +348.5KB | +7.5KB | +6.5KB | +8.0KB | +12.5KB |
-| net462 | 7.0KB | 360.5KB | +353.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net47 | 7.0KB | 360.5KB | +353.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net471 | 8.5KB | 359.5KB | +351.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB |
-| net472 | 8.5KB | 358.0KB | +349.5KB | +8.0KB | +6.5KB | +8.0KB | +12.5KB |
-| net48 | 8.5KB | 358.0KB | +349.5KB | +8.0KB | +6.5KB | +8.0KB | +12.5KB |
-| net481 | 8.5KB | 358.0KB | +349.5KB | +8.0KB | +6.5KB | +8.0KB | +12.5KB |
-| netcoreapp2.0 | 9.0KB | 336.0KB | +327.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
-| netcoreapp2.1 | 9.0KB | 316.0KB | +307.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| netcoreapp2.2 | 9.0KB | 316.0KB | +307.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| netcoreapp3.0 | 9.5KB | 308.5KB | +299.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
-| netcoreapp3.1 | 9.5KB | 307.0KB | +297.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net5.0 | 9.5KB | 271.0KB | +261.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net6.0 | 10.0KB | 212.5KB | +202.5KB | +10.0KB | +7.0KB | +512bytes | +3.5KB |
+| netstandard2.0 | 8.0KB | 358.5KB | +350.5KB | +8.0KB | +7.0KB | +8.0KB | +12.5KB |
+| netstandard2.1 | 8.5KB | 313.0KB | +304.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
+| net461 | 8.5KB | 357.5KB | +349.0KB | +7.5KB | +6.5KB | +8.0KB | +12.0KB |
+| net462 | 7.0KB | 361.0KB | +354.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
+| net47 | 7.0KB | 361.0KB | +354.0KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB |
+| net471 | 8.5KB | 360.0KB | +351.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB |
+| net472 | 8.5KB | 358.5KB | +350.0KB | +7.5KB | +6.5KB | +8.0KB | +12.5KB |
+| net48 | 8.5KB | 358.5KB | +350.0KB | +7.5KB | +6.5KB | +8.0KB | +12.5KB |
+| net481 | 8.5KB | 358.5KB | +350.0KB | +8.0KB | +6.5KB | +8.0KB | +12.5KB |
+| netcoreapp2.0 | 9.0KB | 336.5KB | +327.5KB | +8.5KB | +6.0KB | +8.5KB | +13.5KB |
+| netcoreapp2.1 | 9.0KB | 316.5KB | +307.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
+| netcoreapp2.2 | 9.0KB | 316.5KB | +307.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
+| netcoreapp3.0 | 9.5KB | 309.5KB | +300.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
+| netcoreapp3.1 | 9.5KB | 308.0KB | +298.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
+| net5.0 | 9.5KB | 272.0KB | +262.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
+| net6.0 | 10.0KB | 213.0KB | +203.0KB | +10.0KB | +7.0KB | +512bytes | +3.5KB |
| net7.0 | 10.0KB | 175.0KB | +165.0KB | +11.5KB | +8.0KB | +512bytes | +3.0KB |
| net8.0 | 9.5KB | 145.5KB | +136.0KB | +8.5KB | | +512bytes | +3.0KB |
| net9.0 | 9.5KB | 98.5KB | +89.0KB | +8.5KB | | +512bytes | +3.5KB |
| net10.0 | 10.0KB | 76.0KB | +66.0KB | +8.5KB | | +512bytes | +3.5KB |
-| net11.0 | 10.0KB | 20.5KB | +10.5KB | +9.0KB | | +512bytes | +3.5KB |
+| net11.0 | 10.0KB | 20.5KB | +10.5KB | +9.0KB | | +1.0KB | +3.5KB |
### Assembly Sizes with EmbedUntrackedSources
| | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability |
|----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------|
-| netstandard2.0 | 8.0KB | 524.4KB | +516.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB |
-| netstandard2.1 | 8.5KB | 451.8KB | +443.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net461 | 8.5KB | 524.0KB | +515.5KB | +15.2KB | +8.2KB | +12.9KB | +17.9KB |
-| net462 | 7.0KB | 527.5KB | +520.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net47 | 7.0KB | 527.2KB | +520.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net471 | 8.5KB | 525.8KB | +517.3KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB |
-| net472 | 8.5KB | 523.3KB | +514.8KB | +15.7KB | +8.2KB | +12.9KB | +17.9KB |
-| net48 | 8.5KB | 523.3KB | +514.8KB | +15.7KB | +8.2KB | +12.9KB | +17.9KB |
-| net481 | 8.5KB | 523.3KB | +514.8KB | +15.7KB | +8.2KB | +12.9KB | +17.9KB |
-| netcoreapp2.0 | 9.0KB | 491.3KB | +482.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
-| netcoreapp2.1 | 9.0KB | 459.0KB | +450.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| netcoreapp2.2 | 9.0KB | 459.0KB | +450.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| netcoreapp3.0 | 9.5KB | 442.7KB | +433.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
-| netcoreapp3.1 | 9.5KB | 441.2KB | +431.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net5.0 | 9.5KB | 387.0KB | +377.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net6.0 | 10.0KB | 308.4KB | +298.4KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB |
+| netstandard2.0 | 8.0KB | 524.8KB | +516.8KB | +15.7KB | +8.7KB | +12.9KB | +17.9KB |
+| netstandard2.1 | 8.5KB | 452.7KB | +444.2KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
+| net461 | 8.5KB | 524.9KB | +516.4KB | +15.2KB | +8.2KB | +12.9KB | +17.4KB |
+| net462 | 7.0KB | 528.4KB | +521.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
+| net47 | 7.0KB | 528.1KB | +521.1KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB |
+| net471 | 8.5KB | 526.8KB | +518.3KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB |
+| net472 | 8.5KB | 524.2KB | +515.7KB | +15.2KB | +8.2KB | +12.9KB | +17.9KB |
+| net48 | 8.5KB | 524.2KB | +515.7KB | +15.2KB | +8.2KB | +12.9KB | +17.9KB |
+| net481 | 8.5KB | 524.2KB | +515.7KB | +15.7KB | +8.2KB | +12.9KB | +17.9KB |
+| netcoreapp2.0 | 9.0KB | 492.3KB | +483.3KB | +16.2KB | +7.7KB | +13.4KB | +18.9KB |
+| netcoreapp2.1 | 9.0KB | 459.9KB | +450.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
+| netcoreapp2.2 | 9.0KB | 459.9KB | +450.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
+| netcoreapp3.0 | 9.5KB | 444.0KB | +434.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
+| netcoreapp3.1 | 9.5KB | 442.5KB | +433.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
+| net5.0 | 9.5KB | 388.3KB | +378.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
+| net6.0 | 10.0KB | 309.1KB | +299.1KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB |
| net7.0 | 10.0KB | 252.3KB | +242.3KB | +19.1KB | +9.4KB | +1.1KB | +3.7KB |
| net8.0 | 9.5KB | 207.3KB | +197.8KB | +16.0KB | +299bytes | +1.1KB | +3.7KB |
| net9.0 | 9.5KB | 139.1KB | +129.6KB | +16.0KB | | +1.1KB | +4.2KB |
| net10.0 | 10.0KB | 108.0KB | +98.0KB | +16.0KB | | +1.1KB | +4.2KB |
-| net11.0 | 10.0KB | 30.3KB | +20.3KB | +16.5KB | | +1.1KB | +4.2KB |
+| net11.0 | 10.0KB | 30.4KB | +20.4KB | +16.5KB | | +1.6KB | +4.2KB |
diff --git a/readme.md b/readme.md
index 828151bb..06fbbc90 100644
--- a/readme.md
+++ b/readme.md
@@ -19,28 +19,28 @@ The package targets `netstandard2.0` and is designed to support the following ru
| Target | APIs |
| -- | -- |
-| `net461` | 972 |
-| `net462` | 972 |
-| `net47` | 971 |
-| `net471` | 970 |
-| `net472` | 966 |
-| `net48` | 966 |
-| `net481` | 966 |
-| `netstandard2.0` | 968 |
-| `netstandard2.1` | 821 |
-| `netcoreapp2.0` | 891 |
-| `netcoreapp2.1` | 832 |
-| `netcoreapp2.2` | 832 |
-| `netcoreapp3.0` | 779 |
-| `netcoreapp3.1` | 778 |
-| `net5.0` | 650 |
-| `net6.0` | 555 |
+| `net461` | 973 |
+| `net462` | 973 |
+| `net47` | 972 |
+| `net471` | 971 |
+| `net472` | 967 |
+| `net48` | 967 |
+| `net481` | 967 |
+| `netstandard2.0` | 969 |
+| `netstandard2.1` | 822 |
+| `netcoreapp2.0` | 892 |
+| `netcoreapp2.1` | 833 |
+| `netcoreapp2.2` | 833 |
+| `netcoreapp3.0` | 785 |
+| `netcoreapp3.1` | 784 |
+| `net5.0` | 656 |
+| `net6.0` | 557 |
| `net7.0` | 402 |
| `net8.0` | 283 |
| `net9.0` | 189 |
| `net10.0` | 135 |
| `net11.0` | 58 |
-| `uap10.0` | 958 |
+| `uap10.0` | 959 |
@@ -96,54 +96,54 @@ 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 | 358.5KB | +350.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB |
-| netstandard2.1 | 8.5KB | 312.5KB | +304.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net461 | 8.5KB | 357.0KB | +348.5KB | +7.5KB | +6.5KB | +8.0KB | +12.5KB |
-| net462 | 7.0KB | 360.5KB | +353.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net47 | 7.0KB | 360.5KB | +353.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net471 | 8.5KB | 359.5KB | +351.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB |
-| net472 | 8.5KB | 358.0KB | +349.5KB | +8.0KB | +6.5KB | +8.0KB | +12.5KB |
-| net48 | 8.5KB | 358.0KB | +349.5KB | +8.0KB | +6.5KB | +8.0KB | +12.5KB |
-| net481 | 8.5KB | 358.0KB | +349.5KB | +8.0KB | +6.5KB | +8.0KB | +12.5KB |
-| netcoreapp2.0 | 9.0KB | 336.0KB | +327.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
-| netcoreapp2.1 | 9.0KB | 316.0KB | +307.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| netcoreapp2.2 | 9.0KB | 316.0KB | +307.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| netcoreapp3.0 | 9.5KB | 308.5KB | +299.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB |
-| netcoreapp3.1 | 9.5KB | 307.0KB | +297.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net5.0 | 9.5KB | 271.0KB | +261.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
-| net6.0 | 10.0KB | 212.5KB | +202.5KB | +10.0KB | +7.0KB | +512bytes | +3.5KB |
+| netstandard2.0 | 8.0KB | 358.5KB | +350.5KB | +8.0KB | +7.0KB | +8.0KB | +12.5KB |
+| netstandard2.1 | 8.5KB | 313.0KB | +304.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
+| net461 | 8.5KB | 357.5KB | +349.0KB | +7.5KB | +6.5KB | +8.0KB | +12.0KB |
+| net462 | 7.0KB | 361.0KB | +354.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
+| net47 | 7.0KB | 361.0KB | +354.0KB | +9.0KB | +6.0KB | +9.0KB | +13.5KB |
+| net471 | 8.5KB | 360.0KB | +351.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB |
+| net472 | 8.5KB | 358.5KB | +350.0KB | +7.5KB | +6.5KB | +8.0KB | +12.5KB |
+| net48 | 8.5KB | 358.5KB | +350.0KB | +7.5KB | +6.5KB | +8.0KB | +12.5KB |
+| net481 | 8.5KB | 358.5KB | +350.0KB | +8.0KB | +6.5KB | +8.0KB | +12.5KB |
+| netcoreapp2.0 | 9.0KB | 336.5KB | +327.5KB | +8.5KB | +6.0KB | +8.5KB | +13.5KB |
+| netcoreapp2.1 | 9.0KB | 316.5KB | +307.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
+| netcoreapp2.2 | 9.0KB | 316.5KB | +307.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
+| netcoreapp3.0 | 9.5KB | 309.5KB | +300.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB |
+| netcoreapp3.1 | 9.5KB | 308.0KB | +298.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
+| net5.0 | 9.5KB | 272.0KB | +262.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB |
+| net6.0 | 10.0KB | 213.0KB | +203.0KB | +10.0KB | +7.0KB | +512bytes | +3.5KB |
| net7.0 | 10.0KB | 175.0KB | +165.0KB | +11.5KB | +8.0KB | +512bytes | +3.0KB |
| net8.0 | 9.5KB | 145.5KB | +136.0KB | +8.5KB | | +512bytes | +3.0KB |
| net9.0 | 9.5KB | 98.5KB | +89.0KB | +8.5KB | | +512bytes | +3.5KB |
| net10.0 | 10.0KB | 76.0KB | +66.0KB | +8.5KB | | +512bytes | +3.5KB |
-| net11.0 | 10.0KB | 20.5KB | +10.5KB | +9.0KB | | +512bytes | +3.5KB |
+| net11.0 | 10.0KB | 20.5KB | +10.5KB | +9.0KB | | +1.0KB | +3.5KB |
### Assembly Sizes with EmbedUntrackedSources
| | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability |
|----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------|
-| netstandard2.0 | 8.0KB | 524.4KB | +516.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB |
-| netstandard2.1 | 8.5KB | 451.8KB | +443.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net461 | 8.5KB | 524.0KB | +515.5KB | +15.2KB | +8.2KB | +12.9KB | +17.9KB |
-| net462 | 7.0KB | 527.5KB | +520.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net47 | 7.0KB | 527.2KB | +520.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net471 | 8.5KB | 525.8KB | +517.3KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB |
-| net472 | 8.5KB | 523.3KB | +514.8KB | +15.7KB | +8.2KB | +12.9KB | +17.9KB |
-| net48 | 8.5KB | 523.3KB | +514.8KB | +15.7KB | +8.2KB | +12.9KB | +17.9KB |
-| net481 | 8.5KB | 523.3KB | +514.8KB | +15.7KB | +8.2KB | +12.9KB | +17.9KB |
-| netcoreapp2.0 | 9.0KB | 491.3KB | +482.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
-| netcoreapp2.1 | 9.0KB | 459.0KB | +450.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| netcoreapp2.2 | 9.0KB | 459.0KB | +450.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| netcoreapp3.0 | 9.5KB | 442.7KB | +433.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB |
-| netcoreapp3.1 | 9.5KB | 441.2KB | +431.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net5.0 | 9.5KB | 387.0KB | +377.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
-| net6.0 | 10.0KB | 308.4KB | +298.4KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB |
+| netstandard2.0 | 8.0KB | 524.8KB | +516.8KB | +15.7KB | +8.7KB | +12.9KB | +17.9KB |
+| netstandard2.1 | 8.5KB | 452.7KB | +444.2KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
+| net461 | 8.5KB | 524.9KB | +516.4KB | +15.2KB | +8.2KB | +12.9KB | +17.4KB |
+| net462 | 7.0KB | 528.4KB | +521.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
+| net47 | 7.0KB | 528.1KB | +521.1KB | +16.7KB | +7.7KB | +13.9KB | +18.9KB |
+| net471 | 8.5KB | 526.8KB | +518.3KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB |
+| net472 | 8.5KB | 524.2KB | +515.7KB | +15.2KB | +8.2KB | +12.9KB | +17.9KB |
+| net48 | 8.5KB | 524.2KB | +515.7KB | +15.2KB | +8.2KB | +12.9KB | +17.9KB |
+| net481 | 8.5KB | 524.2KB | +515.7KB | +15.7KB | +8.2KB | +12.9KB | +17.9KB |
+| netcoreapp2.0 | 9.0KB | 492.3KB | +483.3KB | +16.2KB | +7.7KB | +13.4KB | +18.9KB |
+| netcoreapp2.1 | 9.0KB | 459.9KB | +450.9KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
+| netcoreapp2.2 | 9.0KB | 459.9KB | +450.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
+| netcoreapp3.0 | 9.5KB | 444.0KB | +434.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB |
+| netcoreapp3.1 | 9.5KB | 442.5KB | +433.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
+| net5.0 | 9.5KB | 388.3KB | +378.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB |
+| net6.0 | 10.0KB | 309.1KB | +299.1KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB |
| net7.0 | 10.0KB | 252.3KB | +242.3KB | +19.1KB | +9.4KB | +1.1KB | +3.7KB |
| net8.0 | 9.5KB | 207.3KB | +197.8KB | +16.0KB | +299bytes | +1.1KB | +3.7KB |
| net9.0 | 9.5KB | 139.1KB | +129.6KB | +16.0KB | | +1.1KB | +4.2KB |
| net10.0 | 10.0KB | 108.0KB | +98.0KB | +16.0KB | | +1.1KB | +4.2KB |
-| net11.0 | 10.0KB | 30.3KB | +20.3KB | +16.5KB | | +1.1KB | +4.2KB |
+| net11.0 | 10.0KB | 30.4KB | +20.4KB | +16.5KB | | +1.6KB | +4.2KB |
diff --git a/src/Polyfill/ReadOnlySequenceStream.cs b/src/Polyfill/ReadOnlySequenceStream.cs
index 04057be0..4e3d97e8 100644
--- a/src/Polyfill/ReadOnlySequenceStream.cs
+++ b/src/Polyfill/ReadOnlySequenceStream.cs
@@ -16,7 +16,7 @@ namespace System.Buffers;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -35,10 +35,8 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- // Incremental cursor into the sequence's segments, kept in sync with the absolute position.
- // Advancing from this cursor avoids re-walking the segment list from the start on every read.
- SequencePosition cursor;
- long position;
+ // Cursor into the sequence segments. Reads advance it forward; it can never be moved back.
+ SequencePosition position;
bool disposed;
///
@@ -48,47 +46,33 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ // Intentionally non-seekable, matching the BCL (dotnet/runtime#132023): backward positioning would
+ // have to walk the segments again from the start, making repeated seeks worst case O(N), and segment
+ // boundaries can be indirectly controlled by an untrusted network client through packet framing.
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ // Length and Position stay unsupported to match the standard contract for streams where CanSeek is
+ // false, even though the underlying sequence could supply the length cheaply.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
- ///
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
-
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
@@ -97,12 +81,7 @@ public override int Read(byte[] buffer, int offset, int count)
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
-
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
@@ -110,8 +89,7 @@ public override int Read(byte[] buffer, int offset, int count)
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
@@ -120,14 +98,14 @@ public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
@@ -145,76 +123,33 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
-
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
-
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
-
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
-
- MoveTo(newPosition);
- return position;
- }
-
- // Repositions the segment cursor to the given absolute position, advancing forward from the
- // current cursor when possible and only walking from the start for backward jumps.
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
-
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
- // Stream.CopyTo(Stream, int) only became virtual in netcoreapp2.1/netstandard2.1. On older
- // targets it cannot be overridden, so the base implementation (which routes through the
- // cursor-based Read above) is used instead.
+ // Writing a segment straight through needs Stream.Write(ReadOnlySpan). On older targets that
+ // overload does not exist, so the base implementation (which routes through the cursor-based Read
+ // above) is used instead.
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
#endif
@@ -229,23 +164,23 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
@@ -321,7 +256,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
diff --git a/src/Split/net10.0/ReadOnlySequenceStream.cs b/src/Split/net10.0/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/net10.0/ReadOnlySequenceStream.cs
+++ b/src/Split/net10.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net461/ReadOnlySequenceStream.cs b/src/Split/net461/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/net461/ReadOnlySequenceStream.cs
+++ b/src/Split/net461/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net462/ReadOnlySequenceStream.cs b/src/Split/net462/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/net462/ReadOnlySequenceStream.cs
+++ b/src/Split/net462/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net47/ReadOnlySequenceStream.cs b/src/Split/net47/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/net47/ReadOnlySequenceStream.cs
+++ b/src/Split/net47/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net471/ReadOnlySequenceStream.cs b/src/Split/net471/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/net471/ReadOnlySequenceStream.cs
+++ b/src/Split/net471/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net472/ReadOnlySequenceStream.cs b/src/Split/net472/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/net472/ReadOnlySequenceStream.cs
+++ b/src/Split/net472/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net48/ReadOnlySequenceStream.cs b/src/Split/net48/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/net48/ReadOnlySequenceStream.cs
+++ b/src/Split/net48/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net481/ReadOnlySequenceStream.cs b/src/Split/net481/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/net481/ReadOnlySequenceStream.cs
+++ b/src/Split/net481/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net5.0/ReadOnlySequenceStream.cs b/src/Split/net5.0/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/net5.0/ReadOnlySequenceStream.cs
+++ b/src/Split/net5.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net6.0/ReadOnlySequenceStream.cs b/src/Split/net6.0/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/net6.0/ReadOnlySequenceStream.cs
+++ b/src/Split/net6.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net7.0/ReadOnlySequenceStream.cs b/src/Split/net7.0/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/net7.0/ReadOnlySequenceStream.cs
+++ b/src/Split/net7.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net8.0/ReadOnlySequenceStream.cs b/src/Split/net8.0/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/net8.0/ReadOnlySequenceStream.cs
+++ b/src/Split/net8.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/net9.0/ReadOnlySequenceStream.cs b/src/Split/net9.0/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/net9.0/ReadOnlySequenceStream.cs
+++ b/src/Split/net9.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/netcoreapp2.0/ReadOnlySequenceStream.cs b/src/Split/netcoreapp2.0/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/netcoreapp2.0/ReadOnlySequenceStream.cs
+++ b/src/Split/netcoreapp2.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/netcoreapp2.1/ReadOnlySequenceStream.cs b/src/Split/netcoreapp2.1/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/netcoreapp2.1/ReadOnlySequenceStream.cs
+++ b/src/Split/netcoreapp2.1/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/netcoreapp2.2/ReadOnlySequenceStream.cs b/src/Split/netcoreapp2.2/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/netcoreapp2.2/ReadOnlySequenceStream.cs
+++ b/src/Split/netcoreapp2.2/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/netcoreapp3.0/ReadOnlySequenceStream.cs b/src/Split/netcoreapp3.0/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/netcoreapp3.0/ReadOnlySequenceStream.cs
+++ b/src/Split/netcoreapp3.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/netcoreapp3.1/ReadOnlySequenceStream.cs b/src/Split/netcoreapp3.1/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/netcoreapp3.1/ReadOnlySequenceStream.cs
+++ b/src/Split/netcoreapp3.1/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/netstandard2.0/ReadOnlySequenceStream.cs b/src/Split/netstandard2.0/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/netstandard2.0/ReadOnlySequenceStream.cs
+++ b/src/Split/netstandard2.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/netstandard2.1/ReadOnlySequenceStream.cs b/src/Split/netstandard2.1/ReadOnlySequenceStream.cs
index c8adc7a4..4d37bbac 100644
--- a/src/Split/netstandard2.1/ReadOnlySequenceStream.cs
+++ b/src/Split/netstandard2.1/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,60 +94,25 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override void CopyTo(Stream destination, int bufferSize)
{
GuardCopyTo(destination, bufferSize);
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return;
}
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
destination.Write(segment.Span);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
@@ -182,20 +123,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -238,7 +179,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Split/uap10.0/ReadOnlySequenceStream.cs b/src/Split/uap10.0/ReadOnlySequenceStream.cs
index ee49714e..19276a20 100644
--- a/src/Split/uap10.0/ReadOnlySequenceStream.cs
+++ b/src/Split/uap10.0/ReadOnlySequenceStream.cs
@@ -11,7 +11,7 @@ namespace System.Buffers;
using System.Threading;
using System.Threading.Tasks;
///
-/// Provides a seekable, read-only over a .
+/// Provides a read-only, non-seekable for reading from a .
///
///
/// The underlying sequence is not copied; reads are served directly from its segments.
@@ -29,8 +29,7 @@ sealed class ReadOnlySequenceStream :
Stream
{
ReadOnlySequence sequence;
- SequencePosition cursor;
- long position;
+ SequencePosition position;
bool disposed;
///
/// Initializes a new instance of the class over the specified .
@@ -38,73 +37,50 @@ sealed class ReadOnlySequenceStream :
public ReadOnlySequenceStream(ReadOnlySequence source)
{
sequence = source;
- cursor = source.Start;
- position = 0;
+ position = source.Start;
}
///
public override bool CanRead => !disposed;
- ///
- public override bool CanSeek => !disposed;
+ /// Gets a value indicating whether the stream supports seeking. Always .
+ public override bool CanSeek => false;
///
public override bool CanWrite => false;
- ///
- public override long Length
- {
- get
- {
- ThrowIfDisposed();
- return sequence.Length;
- }
- }
- ///
+ /// Gets the length of the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Length => throw new NotSupportedException("Stream does not support seeking.");
+ /// Gets or sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
public override long Position
{
- get
- {
- ThrowIfDisposed();
- return position;
- }
- set
- {
- ThrowIfDisposed();
- if (value < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(value));
- }
- MoveTo(value);
- }
+ get => throw new NotSupportedException("Stream does not support seeking.");
+ set => throw new NotSupportedException("Stream does not support seeking.");
}
///
public override int Read(byte[] buffer, int offset, int count)
{
GuardRange(buffer, offset, count);
ThrowIfDisposed();
- if (position >= sequence.Length)
- {
- return 0;
- }
- var remaining = sequence.Slice(cursor);
+ var remaining = sequence.Slice(position);
var toRead = (int)Math.Min(remaining.Length, count);
if (toRead <= 0)
{
return 0;
}
remaining.Slice(0, toRead).CopyTo(buffer.AsSpan(offset, toRead));
- cursor = sequence.GetPosition(toRead, cursor);
- position += toRead;
+ position = sequence.GetPosition(toRead, position);
return toRead;
}
///
public override int ReadByte()
{
ThrowIfDisposed();
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return -1;
}
- var result = sequence.Slice(cursor, 1).First.Span[0];
- cursor = sequence.GetPosition(1, cursor);
- position++;
+ var result = remaining.Slice(0, 1).First.Span[0];
+ position = sequence.GetPosition(1, position);
return result;
}
///
@@ -118,45 +94,10 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel
}
return Task.FromResult(Read(buffer, offset, count));
}
- ///
- public override long Seek(long offset, SeekOrigin origin)
- {
- ThrowIfDisposed();
- var basePosition = origin switch
- {
- SeekOrigin.Begin => 0L,
- SeekOrigin.Current => position,
- SeekOrigin.End => sequence.Length,
- _ => throw new ArgumentException("Invalid seek origin.", nameof(origin))
- };
- if (offset > long.MaxValue - basePosition)
- {
- throw new ArgumentOutOfRangeException(nameof(offset));
- }
- var newPosition = basePosition + offset;
- if (newPosition < 0)
- {
- throw new IOException("An attempt was made to move the position before the beginning of the stream.");
- }
- MoveTo(newPosition);
- return position;
- }
- void MoveTo(long value)
- {
- if (value >= sequence.Length)
- {
- cursor = sequence.End;
- }
- else if (value >= position)
- {
- cursor = sequence.GetPosition(value - position, cursor);
- }
- else
- {
- cursor = sequence.GetPosition(value, sequence.Start);
- }
- position = value;
- }
+ /// Sets the position within the stream. Not supported; always throws a .
+ /// In all cases.
+ public override long Seek(long offset, SeekOrigin origin) =>
+ throw new NotSupportedException("Stream does not support seeking.");
///
public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
@@ -166,20 +107,20 @@ public override Task CopyToAsync(Stream destination, int bufferSize, Cancellatio
{
return Task.FromCanceled(cancellationToken);
}
- if (position >= sequence.Length)
+ var remaining = sequence.Slice(position);
+ if (remaining.IsEmpty)
{
return Task.CompletedTask;
}
- return CopyToAsyncCore(destination, cancellationToken);
+ return CopyToAsyncCore(remaining, destination, cancellationToken);
}
- async Task CopyToAsyncCore(Stream destination, CancellationToken cancellationToken)
+ async Task CopyToAsyncCore(ReadOnlySequence remaining, Stream destination, CancellationToken cancellationToken)
{
- foreach (var segment in sequence.Slice(cursor))
+ foreach (var segment in remaining)
{
await WriteSegmentAsync(destination, segment, cancellationToken).ConfigureAwait(false);
}
- cursor = sequence.End;
- position = sequence.Length;
+ position = sequence.End;
}
static Task WriteSegmentAsync(Stream destination, ReadOnlyMemory segment, CancellationToken cancellationToken)
{
@@ -237,7 +178,7 @@ protected override void Dispose(bool disposing)
{
disposed = true;
sequence = default;
- cursor = default;
+ position = default;
base.Dispose(disposing);
}
void ThrowIfDisposed()
diff --git a/src/Tests/PolyfillTests_ReadOnlySequenceStream.cs b/src/Tests/PolyfillTests_ReadOnlySequenceStream.cs
index d3c1c7f5..5b49eacf 100644
--- a/src/Tests/PolyfillTests_ReadOnlySequenceStream.cs
+++ b/src/Tests/PolyfillTests_ReadOnlySequenceStream.cs
@@ -10,9 +10,8 @@ public async Task ReadOnlySequenceStream_Capabilities()
using var stream = new ReadOnlySequenceStream(new ReadOnlySequence(new byte[] { 1, 2, 3 }));
await Assert.That(stream.CanRead).IsTrue();
- await Assert.That(stream.CanSeek).IsTrue();
+ await Assert.That(stream.CanSeek).IsFalse();
await Assert.That(stream.CanWrite).IsFalse();
- await Assert.That(stream.Length).IsEqualTo(3L);
}
[Test]
@@ -31,8 +30,6 @@ public async Task ReadOnlySequenceStream_MultiSegment_ReadsAcrossSegments()
var sequence = CreateMultiSegment(new byte[] { 1, 2, 3 }, new byte[] { 4, 5 }, new byte[] { 6, 7, 8, 9 });
using var stream = new ReadOnlySequenceStream(sequence);
- await Assert.That(stream.Length).IsEqualTo(9L);
-
// Read in small chunks so reads straddle segment boundaries.
using var accumulator = new MemoryStream();
var buffer = new byte[2];
@@ -58,65 +55,33 @@ public async Task ReadOnlySequenceStream_ReadByte_AcrossSegments()
}
[Test]
- public async Task ReadOnlySequenceStream_Seek()
+ public async Task ReadOnlySequenceStream_SeekingThrows()
{
- using var stream = new ReadOnlySequenceStream(new ReadOnlySequence(new byte[] { 1, 2, 3, 4, 5 }));
-
- stream.Seek(3, SeekOrigin.Begin);
- await Assert.That(stream.Position).IsEqualTo(3L);
- await Assert.That(stream.ReadByte()).IsEqualTo(4);
-
- stream.Seek(-2, SeekOrigin.Current);
- await Assert.That(stream.ReadByte()).IsEqualTo(3);
+ using var stream = new ReadOnlySequenceStream(new ReadOnlySequence(new byte[] { 1, 2, 3 }));
- stream.Seek(-1, SeekOrigin.End);
- await Assert.That(stream.ReadByte()).IsEqualTo(5);
+ await Assert.That(() => stream.Length).Throws();
+ await Assert.That(() => stream.Position).Throws();
+ await Assert.That(() => stream.Position = 1).Throws();
+ await Assert.That(() => stream.Seek(0, SeekOrigin.Begin)).Throws();
+ await Assert.That(() => stream.Seek(1, SeekOrigin.Current)).Throws();
+ await Assert.That(() => stream.Seek(0, SeekOrigin.End)).Throws();
}
[Test]
- public async Task ReadOnlySequenceStream_SeekAcrossSegments_RepositionsCursor()
+ public async Task ReadOnlySequenceStream_ReadsAreForwardOnly()
{
- // bytes by index: 0..8 -> 1,2,3,4,5,6,7,8,9 across three segments.
- var sequence = CreateMultiSegment(new byte[] { 1, 2, 3 }, new byte[] { 4, 5 }, new byte[] { 6, 7, 8, 9 });
+ var sequence = CreateMultiSegment(new byte[] { 1, 2, 3 }, new byte[] { 4, 5 });
using var stream = new ReadOnlySequenceStream(sequence);
- // Forward from the start into the third segment.
- stream.Seek(6, SeekOrigin.Begin);
- await Assert.That(stream.Position).IsEqualTo(6L);
- await Assert.That(stream.ReadByte()).IsEqualTo(7);
-
- // Backward into the first segment (walk-from-start branch).
- stream.Position = 1;
+ // A failed seek attempt must not disturb the cursor.
+ await Assert.That(stream.ReadByte()).IsEqualTo(1);
+ await Assert.That(() => stream.Seek(0, SeekOrigin.Begin)).Throws();
await Assert.That(stream.ReadByte()).IsEqualTo(2);
- // Forward relative to the current cursor into the second segment.
- stream.Seek(2, SeekOrigin.Current);
- await Assert.That(stream.ReadByte()).IsEqualTo(5);
-
- // Clamp to the end via End origin.
- stream.Seek(0, SeekOrigin.End);
- await Assert.That(stream.Position).IsEqualTo(9L);
- await Assert.That(stream.ReadByte()).IsEqualTo(-1);
- }
-
- [Test]
- public async Task ReadOnlySequenceStream_SeekBeyondLength_ReadsZero()
- {
- using var stream = new ReadOnlySequenceStream(new ReadOnlySequence(new byte[] { 1, 2, 3 }));
-
- stream.Position = 10;
-
- await Assert.That(stream.Position).IsEqualTo(10L);
- await Assert.That(stream.ReadByte()).IsEqualTo(-1);
- await Assert.That(stream.Read(new byte[4], 0, 4)).IsEqualTo(0);
- }
-
- [Test]
- public async Task ReadOnlySequenceStream_SeekBeforeBeginThrows()
- {
- using var stream = new ReadOnlySequenceStream(new ReadOnlySequence(new byte[] { 1, 2, 3 }));
-
- await Assert.That(() => stream.Seek(-1, SeekOrigin.Begin)).Throws();
+ var rest = new byte[4];
+ await Assert.That(stream.Read(rest, 0, 4)).IsEqualTo(3);
+ await Assert.That(rest).IsEquivalentTo(new byte[] { 3, 4, 5, 0 });
+ await Assert.That(stream.Read(rest, 0, 4)).IsEqualTo(0);
}
[Test]
@@ -145,8 +110,8 @@ public async Task ReadOnlySequenceStream_Empty()
{
using var stream = new ReadOnlySequenceStream(ReadOnlySequence.Empty);
- await Assert.That(stream.Length).IsEqualTo(0L);
await Assert.That(stream.Read(new byte[4], 0, 4)).IsEqualTo(0);
+ await Assert.That(stream.ReadByte()).IsEqualTo(-1);
}
[Test]
@@ -159,7 +124,7 @@ public async Task ReadOnlySequenceStream_CopyTo_MultiSegment()
stream.CopyTo(target);
await Assert.That(target.ToArray()).IsEquivalentTo(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
- await Assert.That(stream.Position).IsEqualTo(9L);
+ await Assert.That(stream.Read(new byte[1], 0, 1)).IsEqualTo(0);
}
[Test]
@@ -175,7 +140,7 @@ public async Task ReadOnlySequenceStream_CopyTo_AfterPartialRead_CopiesRemainder
stream.CopyTo(target);
await Assert.That(target.ToArray()).IsEquivalentTo(new byte[] { 3, 4, 5, 6 });
- await Assert.That(stream.Position).IsEqualTo(6L);
+ await Assert.That(stream.Read(new byte[1], 0, 1)).IsEqualTo(0);
}
[Test]
@@ -188,7 +153,7 @@ public async Task ReadOnlySequenceStream_CopyToAsync_MultiSegment()
await stream.CopyToAsync(target);
await Assert.That(target.ToArray()).IsEquivalentTo(new byte[] { 1, 2, 3, 4, 5 });
- await Assert.That(stream.Position).IsEqualTo(5L);
+ await Assert.That(stream.Read(new byte[1], 0, 1)).IsEqualTo(0);
}
static ReadOnlySequence CreateMultiSegment(params byte[][] parts)