diff --git a/apiCount.include.md b/apiCount.include.md index ce3a4e2c..e502c84a 100644 --- a/apiCount.include.md +++ b/apiCount.include.md @@ -1,28 +1,28 @@ -**API count: 1161** +**API count: 1162** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1069 | -| `net462` | 1069 | -| `net47` | 1068 | -| `net471` | 1067 | -| `net472` | 1063 | -| `net48` | 1063 | -| `net481` | 1063 | -| `netstandard2.0` | 1065 | -| `netstandard2.1` | 898 | -| `netcoreapp2.0` | 988 | -| `netcoreapp2.1` | 909 | -| `netcoreapp2.2` | 909 | -| `netcoreapp3.0` | 870 | -| `netcoreapp3.1` | 869 | -| `net5.0` | 747 | -| `net6.0` | 643 | -| `net7.0` | 502 | -| `net8.0` | 381 | +| `net461` | 1070 | +| `net462` | 1070 | +| `net47` | 1069 | +| `net471` | 1068 | +| `net472` | 1064 | +| `net48` | 1064 | +| `net481` | 1064 | +| `netstandard2.0` | 1066 | +| `netstandard2.1` | 899 | +| `netcoreapp2.0` | 989 | +| `netcoreapp2.1` | 910 | +| `netcoreapp2.2` | 910 | +| `netcoreapp3.0` | 871 | +| `netcoreapp3.1` | 870 | +| `net5.0` | 748 | +| `net6.0` | 644 | +| `net7.0` | 503 | +| `net8.0` | 382 | | `net9.0` | 243 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1055 | +| `uap10.0` | 1056 | diff --git a/api_list.include.md b/api_list.include.md index 870b7479..052722f6 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -464,8 +464,11 @@ * `Task ReadAllTextAsync(string, Encoding, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readalltextasync?view=net-11.0#system-io-file-readalltextasync(system-string-system-text-encoding-system-threading-cancellationtoken)) * `IAsyncEnumerable ReadLinesAsync(string, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readlinesasync?view=net-11.0#system-io-file-readalllinesasync(system-string-system-threading-cancellationtoken)) * `IAsyncEnumerable ReadLinesAsync(string, Encoding, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readlinesasync?view=net-11.0#system-io-file-readalllinesasync(system-string-system-text-encoding-system-threading-cancellationtoken)) + * `void WriteAllBytes(string, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writeallbytes?view=net-11.0#system-io-file-writeallbytes(system-string-system-readonlyspan((system-byte)))) + * Note: Copies the span to an array and uses the array overload, so this allocates where the BCL writes straight from the span. + * Note: Only reached when the argument is already a ReadOnlySpan. A byte array binds to the BCL array overload, exactly as it does without Polyfill. * `Task WriteAllBytesAsync(string, byte[], CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writeallbytesasync?view=net-11.0#system-io-file-writeallbytesasync(system-string-system-byte()-system-threading-cancellationtoken)) - * `Task WriteAllBytesAsync(string, ReadOnlyMemory, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.appendallbytesasync?view=net-11.0#system-io-file-appendallbytesasync(system-string-system-readonlymemory((system-byte))-system-threading-cancellationtoken)) + * `Task WriteAllBytesAsync(string, ReadOnlyMemory, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writeallbytesasync?view=net-11.0#system-io-file-writeallbytesasync(system-string-system-readonlymemory((system-byte))-system-threading-cancellationtoken)) * `Task WriteAllLinesAsync(string, IEnumerable, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writealllinesasync?view=net-11.0#system-io-file-writealllinesasync(system-string-system-collections-generic-ienumerable((system-string))-system-threading-cancellationtoken)) * `Task WriteAllLinesAsync(string, IEnumerable, Encoding, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writealllinesasync?view=net-11.0#system-io-file-writealllinesasync(system-string-system-collections-generic-ienumerable((system-string))-system-text-encoding-system-threading-cancellationtoken)) * `void WriteAllText(string, ReadOnlySpan, Encoding)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writealltext?view=net-11.0#system-io-file-writealltext(system-string-system-readonlyspan((system-char))-system-text-encoding)) diff --git a/assemblySize.include.md b/assemblySize.include.md index fb715bc2..f0111a0b 100644 --- a/assemblySize.include.md +++ b/assemblySize.include.md @@ -3,21 +3,21 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| | netstandard2.0 | 8.0KB | 385.5KB | +377.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 341.5KB | +333.0KB | +9.0KB | +6.5KB | +8.5KB | +13.5KB | -| net461 | 8.5KB | 384.0KB | +375.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 388.0KB | +381.0KB | +8.5KB | +6.0KB | +8.5KB | +13.0KB | +| netstandard2.1 | 8.5KB | 341.5KB | +333.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 384.5KB | +376.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 388.0KB | +381.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | | net47 | 7.0KB | 387.5KB | +380.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net471 | 8.5KB | 386.5KB | +378.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net472 | 8.5KB | 385.5KB | +377.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 385.5KB | +377.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net481 | 8.5KB | 385.5KB | +377.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 363.5KB | +354.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.1 | 9.0KB | 344.0KB | +335.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp2.2 | 9.0KB | 344.0KB | +335.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp3.0 | 9.5KB | 342.5KB | +333.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp3.1 | 9.5KB | 341.0KB | +331.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net5.0 | 9.5KB | 306.5KB | +297.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net6.0 | 10.0KB | 247.5KB | +237.5KB | +9.5KB | +6.5KB | +512bytes | +3.0KB | +| net471 | 8.5KB | 386.5KB | +378.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net472 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 363.5KB | +354.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.1 | 9.0KB | 344.5KB | +335.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 344.5KB | +335.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 342.5KB | +333.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| netcoreapp3.1 | 9.5KB | 341.0KB | +331.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net5.0 | 9.5KB | 306.5KB | +297.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net6.0 | 10.0KB | 247.5KB | +237.5KB | +9.5KB | +7.0KB | +512bytes | +3.5KB | | net7.0 | 10.0KB | 216.0KB | +206.0KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | | net8.0 | 9.5KB | 186.5KB | +177.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB | | net9.0 | 9.5KB | 117.0KB | +107.5KB | +8.0KB | | +512bytes | +3.0KB | @@ -30,22 +30,22 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| | netstandard2.0 | 8.0KB | 561.3KB | +553.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 490.3KB | +481.8KB | +16.7KB | +8.2KB | +13.4KB | +18.9KB | -| net461 | 8.5KB | 560.8KB | +552.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 564.8KB | +557.8KB | +16.2KB | +7.7KB | +13.4KB | +18.4KB | -| net47 | 7.0KB | 564.0KB | +557.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net471 | 8.5KB | 562.7KB | +554.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net472 | 8.5KB | 560.6KB | +552.1KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 560.6KB | +552.1KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net481 | 8.5KB | 560.6KB | +552.1KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 528.7KB | +519.7KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.1 | 9.0KB | 496.5KB | +487.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp2.2 | 9.0KB | 496.5KB | +487.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp3.0 | 9.5KB | 488.3KB | +478.8KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp3.1 | 9.5KB | 486.8KB | +477.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net5.0 | 9.5KB | 434.8KB | +425.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net6.0 | 10.0KB | 355.7KB | +345.7KB | +17.2KB | +8.2KB | +1.1KB | +3.7KB | -| net7.0 | 10.0KB | 307.3KB | +297.3KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | +| netstandard2.1 | 8.5KB | 490.3KB | +481.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 561.3KB | +552.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 564.8KB | +557.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net47 | 7.0KB | 564.1KB | +557.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net471 | 8.5KB | 562.7KB | +554.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net472 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 528.7KB | +519.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.1 | 9.0KB | 497.0KB | +488.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 497.0KB | +488.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 488.3KB | +478.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| netcoreapp3.1 | 9.5KB | 486.8KB | +477.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net5.0 | 9.5KB | 434.9KB | +425.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net6.0 | 10.0KB | 355.7KB | +345.7KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 307.4KB | +297.4KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | | net8.0 | 9.5KB | 264.1KB | +254.6KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | | net9.0 | 9.5KB | 169.1KB | +159.6KB | +15.5KB | | +1.1KB | +3.7KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | diff --git a/readme.md b/readme.md index 247da631..bde9498b 100644 --- a/readme.md +++ b/readme.md @@ -13,34 +13,34 @@ The package targets `netstandard2.0` and is designed to support the following ru * `uap10` -**API count: 1161** +**API count: 1162** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1069 | -| `net462` | 1069 | -| `net47` | 1068 | -| `net471` | 1067 | -| `net472` | 1063 | -| `net48` | 1063 | -| `net481` | 1063 | -| `netstandard2.0` | 1065 | -| `netstandard2.1` | 898 | -| `netcoreapp2.0` | 988 | -| `netcoreapp2.1` | 909 | -| `netcoreapp2.2` | 909 | -| `netcoreapp3.0` | 870 | -| `netcoreapp3.1` | 869 | -| `net5.0` | 747 | -| `net6.0` | 643 | -| `net7.0` | 502 | -| `net8.0` | 381 | +| `net461` | 1070 | +| `net462` | 1070 | +| `net47` | 1069 | +| `net471` | 1068 | +| `net472` | 1064 | +| `net48` | 1064 | +| `net481` | 1064 | +| `netstandard2.0` | 1066 | +| `netstandard2.1` | 899 | +| `netcoreapp2.0` | 989 | +| `netcoreapp2.1` | 910 | +| `netcoreapp2.2` | 910 | +| `netcoreapp3.0` | 871 | +| `netcoreapp3.1` | 870 | +| `net5.0` | 748 | +| `net6.0` | 644 | +| `net7.0` | 503 | +| `net8.0` | 382 | | `net9.0` | 243 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1055 | +| `uap10.0` | 1056 | @@ -97,21 +97,21 @@ 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 | 385.5KB | +377.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 341.5KB | +333.0KB | +9.0KB | +6.5KB | +8.5KB | +13.5KB | -| net461 | 8.5KB | 384.0KB | +375.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 388.0KB | +381.0KB | +8.5KB | +6.0KB | +8.5KB | +13.0KB | +| netstandard2.1 | 8.5KB | 341.5KB | +333.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 384.5KB | +376.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 388.0KB | +381.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | | net47 | 7.0KB | 387.5KB | +380.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net471 | 8.5KB | 386.5KB | +378.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net472 | 8.5KB | 385.5KB | +377.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 385.5KB | +377.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net481 | 8.5KB | 385.5KB | +377.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 363.5KB | +354.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.1 | 9.0KB | 344.0KB | +335.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp2.2 | 9.0KB | 344.0KB | +335.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp3.0 | 9.5KB | 342.5KB | +333.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp3.1 | 9.5KB | 341.0KB | +331.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net5.0 | 9.5KB | 306.5KB | +297.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net6.0 | 10.0KB | 247.5KB | +237.5KB | +9.5KB | +6.5KB | +512bytes | +3.0KB | +| net471 | 8.5KB | 386.5KB | +378.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net472 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 385.5KB | +377.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 363.5KB | +354.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.1 | 9.0KB | 344.5KB | +335.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 344.5KB | +335.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 342.5KB | +333.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| netcoreapp3.1 | 9.5KB | 341.0KB | +331.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net5.0 | 9.5KB | 306.5KB | +297.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | +| net6.0 | 10.0KB | 247.5KB | +237.5KB | +9.5KB | +7.0KB | +512bytes | +3.5KB | | net7.0 | 10.0KB | 216.0KB | +206.0KB | +9.5KB | +6.0KB | +512bytes | +3.5KB | | net8.0 | 9.5KB | 186.5KB | +177.0KB | +8.5KB | +512bytes | +512bytes | +3.5KB | | net9.0 | 9.5KB | 117.0KB | +107.5KB | +8.0KB | | +512bytes | +3.0KB | @@ -124,22 +124,22 @@ 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 | 561.3KB | +553.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 490.3KB | +481.8KB | +16.7KB | +8.2KB | +13.4KB | +18.9KB | -| net461 | 8.5KB | 560.8KB | +552.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 564.8KB | +557.8KB | +16.2KB | +7.7KB | +13.4KB | +18.4KB | -| net47 | 7.0KB | 564.0KB | +557.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net471 | 8.5KB | 562.7KB | +554.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net472 | 8.5KB | 560.6KB | +552.1KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 560.6KB | +552.1KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net481 | 8.5KB | 560.6KB | +552.1KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 528.7KB | +519.7KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.1 | 9.0KB | 496.5KB | +487.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp2.2 | 9.0KB | 496.5KB | +487.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp3.0 | 9.5KB | 488.3KB | +478.8KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp3.1 | 9.5KB | 486.8KB | +477.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net5.0 | 9.5KB | 434.8KB | +425.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net6.0 | 10.0KB | 355.7KB | +345.7KB | +17.2KB | +8.2KB | +1.1KB | +3.7KB | -| net7.0 | 10.0KB | 307.3KB | +297.3KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | +| netstandard2.1 | 8.5KB | 490.3KB | +481.8KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 561.3KB | +552.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 564.8KB | +557.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net47 | 7.0KB | 564.1KB | +557.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net471 | 8.5KB | 562.7KB | +554.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net472 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 560.6KB | +552.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 528.7KB | +519.7KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.1 | 9.0KB | 497.0KB | +488.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 497.0KB | +488.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 488.3KB | +478.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| netcoreapp3.1 | 9.5KB | 486.8KB | +477.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net5.0 | 9.5KB | 434.9KB | +425.4KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | +| net6.0 | 10.0KB | 355.7KB | +345.7KB | +17.2KB | +8.7KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 307.4KB | +297.4KB | +17.1KB | +7.4KB | +1.1KB | +4.2KB | | net8.0 | 9.5KB | 264.1KB | +254.6KB | +16.0KB | +811bytes | +1.1KB | +4.2KB | | net9.0 | 9.5KB | 169.1KB | +159.6KB | +15.5KB | | +1.1KB | +3.7KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | @@ -1079,8 +1079,11 @@ The class `Polyfill` includes the following extension methods: * `Task ReadAllTextAsync(string, Encoding, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readalltextasync?view=net-11.0#system-io-file-readalltextasync(system-string-system-text-encoding-system-threading-cancellationtoken)) * `IAsyncEnumerable ReadLinesAsync(string, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readlinesasync?view=net-11.0#system-io-file-readalllinesasync(system-string-system-threading-cancellationtoken)) * `IAsyncEnumerable ReadLinesAsync(string, Encoding, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readlinesasync?view=net-11.0#system-io-file-readalllinesasync(system-string-system-text-encoding-system-threading-cancellationtoken)) + * `void WriteAllBytes(string, ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writeallbytes?view=net-11.0#system-io-file-writeallbytes(system-string-system-readonlyspan((system-byte)))) + * Note: Copies the span to an array and uses the array overload, so this allocates where the BCL writes straight from the span. + * Note: Only reached when the argument is already a ReadOnlySpan. A byte array binds to the BCL array overload, exactly as it does without Polyfill. * `Task WriteAllBytesAsync(string, byte[], CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writeallbytesasync?view=net-11.0#system-io-file-writeallbytesasync(system-string-system-byte()-system-threading-cancellationtoken)) - * `Task WriteAllBytesAsync(string, ReadOnlyMemory, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.appendallbytesasync?view=net-11.0#system-io-file-appendallbytesasync(system-string-system-readonlymemory((system-byte))-system-threading-cancellationtoken)) + * `Task WriteAllBytesAsync(string, ReadOnlyMemory, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writeallbytesasync?view=net-11.0#system-io-file-writeallbytesasync(system-string-system-readonlymemory((system-byte))-system-threading-cancellationtoken)) * `Task WriteAllLinesAsync(string, IEnumerable, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writealllinesasync?view=net-11.0#system-io-file-writealllinesasync(system-string-system-collections-generic-ienumerable((system-string))-system-threading-cancellationtoken)) * `Task WriteAllLinesAsync(string, IEnumerable, Encoding, CancellationToken)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writealllinesasync?view=net-11.0#system-io-file-writealllinesasync(system-string-system-collections-generic-ienumerable((system-string))-system-text-encoding-system-threading-cancellationtoken)) * `void WriteAllText(string, ReadOnlySpan, Encoding)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writealltext?view=net-11.0#system-io-file-writealltext(system-string-system-readonlyspan((system-char))-system-text-encoding)) diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index be619f68..202fdef0 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -1192,6 +1192,11 @@ void File_Methods() var sourceContent = "Test content"; File.WriteAllText(TestFilePath, sourceContent); +#if FeatureMemory + ReadOnlySpan byteSpan = new byte[] { 1, 2, 3 }; + File.WriteAllBytes(TestFilePath, byteSpan); +#endif + var fileMode = File.GetUnixFileMode(TestFilePath); // Use the | bitwise OR operator to combine multiple file modes diff --git a/src/Polyfill/FilePolyfill.cs b/src/Polyfill/FilePolyfill.cs index 4ae35518..4446d404 100644 --- a/src/Polyfill/FilePolyfill.cs +++ b/src/Polyfill/FilePolyfill.cs @@ -83,13 +83,23 @@ public static void AppendAllBytes(string path, ReadOnlySpan bytes) /// /// Asynchronously creates a new file, writes the specified byte array to the file, and then closes the file. If the target file already exists, it is truncated and overwritten. /// - //Link: https://learn.microsoft.com/en-us/dotnet/api/system.io.file.appendallbytesasync?view=net-11.0#system-io-file-appendallbytesasync(system-string-system-readonlymemory((system-byte))-system-threading-cancellationtoken) + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writeallbytesasync?view=net-11.0#system-io-file-writeallbytesasync(system-string-system-readonlymemory((system-byte))-system-threading-cancellationtoken) public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory bytes, CancellationToken cancellationToken = default) { using var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true); await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } + /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.io.file.writeallbytes?view=net-11.0#system-io-file-writeallbytes(system-string-system-readonlyspan((system-byte))) + //Note: Copies the span to an array and uses the array overload, so this allocates where the BCL writes straight from the span. + //Note: Only reached when the argument is already a ReadOnlySpan. A byte array binds to the BCL array overload, exactly as it does without Polyfill. + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. diff --git a/src/Split/net461/FilePolyfill.cs b/src/Split/net461/FilePolyfill.cs index c6112e74..e90c5b16 100644 --- a/src/Split/net461/FilePolyfill.cs +++ b/src/Split/net461/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net462/FilePolyfill.cs b/src/Split/net462/FilePolyfill.cs index c6112e74..e90c5b16 100644 --- a/src/Split/net462/FilePolyfill.cs +++ b/src/Split/net462/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net47/FilePolyfill.cs b/src/Split/net47/FilePolyfill.cs index c6112e74..e90c5b16 100644 --- a/src/Split/net47/FilePolyfill.cs +++ b/src/Split/net47/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net471/FilePolyfill.cs b/src/Split/net471/FilePolyfill.cs index c6112e74..e90c5b16 100644 --- a/src/Split/net471/FilePolyfill.cs +++ b/src/Split/net471/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net472/FilePolyfill.cs b/src/Split/net472/FilePolyfill.cs index c6112e74..e90c5b16 100644 --- a/src/Split/net472/FilePolyfill.cs +++ b/src/Split/net472/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net48/FilePolyfill.cs b/src/Split/net48/FilePolyfill.cs index c6112e74..e90c5b16 100644 --- a/src/Split/net48/FilePolyfill.cs +++ b/src/Split/net48/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net481/FilePolyfill.cs b/src/Split/net481/FilePolyfill.cs index c6112e74..e90c5b16 100644 --- a/src/Split/net481/FilePolyfill.cs +++ b/src/Split/net481/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net5.0/FilePolyfill.cs b/src/Split/net5.0/FilePolyfill.cs index ff68c4d3..609e0b7f 100644 --- a/src/Split/net5.0/FilePolyfill.cs +++ b/src/Split/net5.0/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net6.0/FilePolyfill.cs b/src/Split/net6.0/FilePolyfill.cs index 87911de4..f6cd267c 100644 --- a/src/Split/net6.0/FilePolyfill.cs +++ b/src/Split/net6.0/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net7.0/FilePolyfill.cs b/src/Split/net7.0/FilePolyfill.cs index cd559610..02532880 100644 --- a/src/Split/net7.0/FilePolyfill.cs +++ b/src/Split/net7.0/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/net8.0/FilePolyfill.cs b/src/Split/net8.0/FilePolyfill.cs index cd559610..02532880 100644 --- a/src/Split/net8.0/FilePolyfill.cs +++ b/src/Split/net8.0/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/netcoreapp2.0/FilePolyfill.cs b/src/Split/netcoreapp2.0/FilePolyfill.cs index 3e55baa2..1fe06419 100644 --- a/src/Split/netcoreapp2.0/FilePolyfill.cs +++ b/src/Split/netcoreapp2.0/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/netcoreapp2.1/FilePolyfill.cs b/src/Split/netcoreapp2.1/FilePolyfill.cs index 3e55baa2..1fe06419 100644 --- a/src/Split/netcoreapp2.1/FilePolyfill.cs +++ b/src/Split/netcoreapp2.1/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/netcoreapp2.2/FilePolyfill.cs b/src/Split/netcoreapp2.2/FilePolyfill.cs index 3e55baa2..1fe06419 100644 --- a/src/Split/netcoreapp2.2/FilePolyfill.cs +++ b/src/Split/netcoreapp2.2/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/netcoreapp3.0/FilePolyfill.cs b/src/Split/netcoreapp3.0/FilePolyfill.cs index ff68c4d3..609e0b7f 100644 --- a/src/Split/netcoreapp3.0/FilePolyfill.cs +++ b/src/Split/netcoreapp3.0/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/netcoreapp3.1/FilePolyfill.cs b/src/Split/netcoreapp3.1/FilePolyfill.cs index ff68c4d3..609e0b7f 100644 --- a/src/Split/netcoreapp3.1/FilePolyfill.cs +++ b/src/Split/netcoreapp3.1/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/netstandard2.0/FilePolyfill.cs b/src/Split/netstandard2.0/FilePolyfill.cs index 6dda0398..68eb9a13 100644 --- a/src/Split/netstandard2.0/FilePolyfill.cs +++ b/src/Split/netstandard2.0/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/netstandard2.1/FilePolyfill.cs b/src/Split/netstandard2.1/FilePolyfill.cs index 3e55baa2..1fe06419 100644 --- a/src/Split/netstandard2.1/FilePolyfill.cs +++ b/src/Split/netstandard2.1/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Split/uap10.0/FilePolyfill.cs b/src/Split/uap10.0/FilePolyfill.cs index 6dda0398..68eb9a13 100644 --- a/src/Split/uap10.0/FilePolyfill.cs +++ b/src/Split/uap10.0/FilePolyfill.cs @@ -73,6 +73,12 @@ public static async Task WriteAllBytesAsync(string path, ReadOnlyMemory by await stream.WriteAsync(bytes.ToArray(), 0, bytes.Length, cancellationToken); } /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. + /// If the target file already exists, it is truncated and overwritten. + /// + public static void WriteAllBytes(string path, ReadOnlySpan bytes) => + File.WriteAllBytes(path, bytes.ToArray()); + /// /// Creates a new file, writes the specified string to the file, and then closes the file. /// If the target file already exists, it is truncated and overwritten. /// diff --git a/src/Tests/FileWriteAllBytesSpanTests.cs b/src/Tests/FileWriteAllBytesSpanTests.cs new file mode 100644 index 00000000..2b766df6 --- /dev/null +++ b/src/Tests/FileWriteAllBytesSpanTests.cs @@ -0,0 +1,125 @@ +#if FeatureMemory + +using System.IO; + +// Runs on every target, so net9.0 and later validate the BCL and everything below the polyfill. +// The byte array overload is the oracle: it exists unchanged on every framework and writes the +// bytes this span overload exists to write without allocating. +public class FileWriteAllBytesSpanTests +{ + [Test] + public async Task MatchesTheArrayOverload() + { + using var directory = new TempDirectory(); + + byte[][] payloads = [[], [1], [1, 2, 3], new byte[100000]]; + foreach (var payload in payloads) + { + var viaSpan = directory.File("span.bin"); + var viaArray = directory.File("array.bin"); + + WriteSpan(viaSpan, payload); + File.WriteAllBytes(viaArray, payload); + + await Assert.That(File.ReadAllBytes(viaSpan)).IsEquivalentTo(File.ReadAllBytes(viaArray)); + } + } + + [Test] + public async Task TruncatesAnExistingFile() + { + using var directory = new TempDirectory(); + var path = directory.File("truncate.bin"); + + File.WriteAllBytes(path, new byte[50]); + WriteSpan(path, [1, 2, 3]); + await Assert.That(new FileInfo(path).Length).IsEqualTo(3); + + WriteSpan(path, []); + await Assert.That(new FileInfo(path).Length).IsEqualTo(0); + await Assert.That(File.Exists(path)).IsTrue(); + } + + [Test] + public async Task CreatesAMissingFile() + { + using var directory = new TempDirectory(); + var path = directory.File("fresh.bin"); + + WriteSpan(path, [9]); + + await Assert.That(File.Exists(path)).IsTrue(); + await Assert.That(File.ReadAllBytes(path)).IsEquivalentTo(new byte[] { 9 }); + } + + [Test] + public async Task ArgumentValidationMatchesTheArrayOverload() + { + using var directory = new TempDirectory(); + var missingDirectory = Path.Combine(directory.Path, "nope", "x.bin"); + + await Assert.That(CaptureSpan(null!)).IsEqualTo(CaptureArray(null!)); + await Assert.That(CaptureSpan("")).IsEqualTo(CaptureArray("")); + await Assert.That(CaptureSpan(" ")).IsEqualTo(CaptureArray(" ")); + await Assert.That(CaptureSpan(missingDirectory)).IsEqualTo(CaptureArray(missingDirectory)); + + // and the concrete shapes, so a change in either overload is visible. Only the ones that + // are the same everywhere are pinned with their paramName: .NET Framework throws + // ArgumentException with no paramName for an empty path where .NET names it, and the + // polyfill inherits whichever the running framework does. + await Assert.That(CaptureSpan(null!)).IsEqualTo("ArgumentNullException:path"); + await Assert.That(CaptureSpan(missingDirectory)).IsEqualTo("DirectoryNotFoundException:"); + await Assert.That(TypeOfSpanFailure("")).IsEqualTo("ArgumentException"); + await Assert.That(TypeOfSpanFailure(" ")).IsEqualTo("ArgumentException"); + } + + // the span stays inside these helpers, so nothing ref struct shaped lives across an await + static void WriteSpan(string path, byte[] bytes) => + File.WriteAllBytes(path, (ReadOnlySpan) bytes); + + static string CaptureSpan(string path) => + Capture(() => File.WriteAllBytes(path, (ReadOnlySpan) new byte[] { 1 })); + + static string TypeOfSpanFailure(string path) => + CaptureSpan(path).Split(':')[0]; + + static string CaptureArray(string path) => + Capture(() => File.WriteAllBytes(path, new byte[] { 1 })); + + static string Capture(Action action) + { + try + { + action(); + return "none"; + } + catch (Exception exception) + { + return $"{exception.GetType().Name}:{(exception as ArgumentException)?.ParamName}"; + } + } + + class TempDirectory : IDisposable + { + public string Path { get; } = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "polyfill-" + Guid.NewGuid().ToString("N")); + + public TempDirectory() => + Directory.CreateDirectory(Path); + + public string File(string name) => + System.IO.Path.Combine(Path, name); + + public void Dispose() + { + try + { + Directory.Delete(Path, true); + } + catch + { + } + } + } +} + +#endif