From 50c32ddce9ff32635b6a9a20dc82d7f085452dce Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 10 Sep 2026 16:12:45 +1000 Subject: [PATCH] Add the BitConverter span overloads for netstandard2.0 and .NET Framework Twenty members: ToBoolean, ToChar, ToInt16, ToInt32, ToInt64, ToSingle, ToDouble, ToUInt16, ToUInt32 and ToUInt64 over a ReadOnlySpan, and the matching TryWriteBytes. Confirmed against the reference assemblies to be missing on net461 through net481, netstandard2.0 and netcoreapp2.0, and present from netstandard2.1 and netcoreapp2.1, so the guard is the pattern already used elsewhere in the repo. Reads and writes go through MemoryMarshal, as BinaryPrimitivesPolyfill already does, which reads unaligned and does not copy. ToBoolean is done by hand rather than reinterpreting the byte, since any non-zero byte is true and a non-canonical bool is not something to create deliberately. Verified against net11: a short span is rejected with a plain ArgumentOutOfRangeException naming value, not the ArgumentException the array overloads use for the same shape; TryWriteBytes leaves the destination untouched when it refuses; and a larger destination is written only up to the width of the value. The tests read at odd offsets, which would fault or misread under an implementation that assumed alignment, and run on every target, so netstandard2.1 and later check the BCL while net462 checks the polyfill. API count 1119 -> 1139. --- apiCount.include.md | 22 +- api_list.include.md | 20 ++ assemblySize.include.md | 40 +-- readme.md | 86 +++--- src/Consume/Consume.cs | 30 ++ src/Polyfill/Polyfill_BitConverter.cs | 268 ++++++++++++++++++ src/Split/net461/Polyfill_BitConverter.cs | 207 ++++++++++++++ src/Split/net462/Polyfill_BitConverter.cs | 207 ++++++++++++++ src/Split/net47/Polyfill_BitConverter.cs | 207 ++++++++++++++ src/Split/net471/Polyfill_BitConverter.cs | 207 ++++++++++++++ src/Split/net472/Polyfill_BitConverter.cs | 207 ++++++++++++++ src/Split/net48/Polyfill_BitConverter.cs | 207 ++++++++++++++ src/Split/net481/Polyfill_BitConverter.cs | 207 ++++++++++++++ .../netcoreapp2.0/Polyfill_BitConverter.cs | 207 ++++++++++++++ .../netstandard2.0/Polyfill_BitConverter.cs | 207 ++++++++++++++ src/Split/uap10.0/Polyfill_BitConverter.cs | 207 ++++++++++++++ src/Tests/BitConverterSpanTests.cs | 177 ++++++++++++ 17 files changed, 2649 insertions(+), 64 deletions(-) create mode 100644 src/Tests/BitConverterSpanTests.cs diff --git a/apiCount.include.md b/apiCount.include.md index 5712bcdc..8fa93ace 100644 --- a/apiCount.include.md +++ b/apiCount.include.md @@ -1,19 +1,19 @@ -**API count: 1119** +**API count: 1139** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1038 | -| `net462` | 1038 | -| `net47` | 1037 | -| `net471` | 1036 | -| `net472` | 1032 | -| `net48` | 1032 | -| `net481` | 1032 | -| `netstandard2.0` | 1034 | +| `net461` | 1058 | +| `net462` | 1058 | +| `net47` | 1057 | +| `net471` | 1056 | +| `net472` | 1052 | +| `net48` | 1052 | +| `net481` | 1052 | +| `netstandard2.0` | 1054 | | `netstandard2.1` | 887 | -| `netcoreapp2.0` | 957 | +| `netcoreapp2.0` | 977 | | `netcoreapp2.1` | 898 | | `netcoreapp2.2` | 898 | | `netcoreapp3.0` | 859 | @@ -25,4 +25,4 @@ | `net9.0` | 238 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1024 | +| `uap10.0` | 1044 | diff --git a/api_list.include.md b/api_list.include.md index d05b43d2..d1ceac91 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -98,12 +98,32 @@ * `float Int32BitsToSingle(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.int32bitstosingle?view=net-11.0) * `int SingleToInt32Bits(float)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.singletoint32bits?view=net-11.0) * `uint SingleToUInt32Bits(float)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.singletouint32bits?view=net-11.0) + * `bool ToBoolean(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toboolean?view=net-11.0#system-bitconverter-toboolean(system-readonlyspan((system-byte)))) + * `char ToChar(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.tochar?view=net-11.0#system-bitconverter-tochar(system-readonlyspan((system-byte)))) + * `double ToDouble(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.todouble?view=net-11.0#system-bitconverter-todouble(system-readonlyspan((system-byte)))) * `Int128 ToInt128(byte[], int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint128?view=net-11.0#system-bitconverter-toint128(system-byte()-system-int32)) * `Int128 ToInt128(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint128?view=net-11.0#system-bitconverter-toint128(system-readonlyspan((system-byte)))) + * `short ToInt16(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint16?view=net-11.0#system-bitconverter-toint16(system-readonlyspan((system-byte)))) + * `int ToInt32(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint32?view=net-11.0#system-bitconverter-toint32(system-readonlyspan((system-byte)))) + * `long ToInt64(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint64?view=net-11.0#system-bitconverter-toint64(system-readonlyspan((system-byte)))) + * `float ToSingle(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.tosingle?view=net-11.0#system-bitconverter-tosingle(system-readonlyspan((system-byte)))) * `UInt128 ToUInt128(byte[], int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint128?view=net-11.0#system-bitconverter-touint128(system-byte()-system-int32)) * `UInt128 ToUInt128(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint128?view=net-11.0#system-bitconverter-touint128(system-readonlyspan((system-byte)))) + * `ushort ToUInt16(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint16?view=net-11.0#system-bitconverter-touint16(system-readonlyspan((system-byte)))) + * `uint ToUInt32(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint32?view=net-11.0#system-bitconverter-touint32(system-readonlyspan((system-byte)))) + * `ulong ToUInt64(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint64?view=net-11.0#system-bitconverter-touint64(system-readonlyspan((system-byte)))) + * `bool TryWriteBytes(Span, bool)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-boolean)) + * `bool TryWriteBytes(Span, char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-char)) + * `bool TryWriteBytes(Span, double)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-double)) + * `bool TryWriteBytes(Span, float)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-single)) + * `bool TryWriteBytes(Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int32)) * `bool TryWriteBytes(Span, Int128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int128)) + * `bool TryWriteBytes(Span, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int64)) + * `bool TryWriteBytes(Span, short)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int16)) + * `bool TryWriteBytes(Span, uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint32)) * `bool TryWriteBytes(Span, UInt128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint128)) + * `bool TryWriteBytes(Span, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint64)) + * `bool TryWriteBytes(Span, ushort)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint16)) * `float UInt32BitsToSingle(uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.uint32bitstosingle?view=net-11.0) * `double UInt64BitsToDouble(ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.uint64bitstodouble?view=net-11.0) diff --git a/assemblySize.include.md b/assemblySize.include.md index 91d26394..637aa729 100644 --- a/assemblySize.include.md +++ b/assemblySize.include.md @@ -2,16 +2,16 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 371.5KB | +363.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.0 | 8.0KB | 373.5KB | +365.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | | netstandard2.1 | 8.5KB | 327.0KB | +318.5KB | +8.5KB | +6.5KB | +8.5KB | +13.5KB | -| net461 | 8.5KB | 370.0KB | +361.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 375.0KB | +368.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net47 | 7.0KB | 375.0KB | +368.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net471 | 8.5KB | 372.5KB | +364.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net472 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net48 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net481 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| netcoreapp2.0 | 9.0KB | 350.5KB | +341.5KB | +7.5KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 372.5KB | +364.0KB | +8.5KB | +6.0KB | +8.5KB | +13.0KB | +| net462 | 7.0KB | 377.5KB | +370.5KB | +7.0KB | +6.5KB | +7.5KB | +11.5KB | +| net47 | 7.0KB | 377.0KB | +370.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 374.5KB | +366.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net472 | 8.5KB | 373.5KB | +365.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 373.5KB | +365.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 373.5KB | +365.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 352.5KB | +343.5KB | +7.5KB | +6.5KB | +9.0KB | +12.0KB | | netcoreapp2.1 | 9.0KB | 330.5KB | +321.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | | netcoreapp2.2 | 9.0KB | 330.5KB | +321.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | | netcoreapp3.0 | 9.5KB | 326.5KB | +317.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | @@ -19,7 +19,7 @@ | net5.0 | 9.5KB | 288.5KB | +279.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | | net6.0 | 10.0KB | 230.0KB | +220.0KB | +10.0KB | +7.0KB | +1.0KB | +3.5KB | | net7.0 | 10.0KB | 199.0KB | +189.0KB | +9.0KB | +5.5KB | +512bytes | +3.0KB | -| net8.0 | 9.5KB | 168.5KB | +159.0KB | +8.5KB | +512bytes | +1.0KB | +3.5KB | +| net8.0 | 9.5KB | 169.0KB | +159.5KB | +8.0KB | | +512bytes | +3.0KB | | net9.0 | 9.5KB | 116.0KB | +106.5KB | +8.0KB | | +512bytes | +3.0KB | | net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -29,16 +29,16 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 544.9KB | +536.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.0 | 8.0KB | 547.4KB | +539.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | | netstandard2.1 | 8.5KB | 473.8KB | +465.3KB | +16.2KB | +8.2KB | +13.4KB | +18.9KB | -| net461 | 8.5KB | 544.5KB | +536.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 549.5KB | +542.5KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net47 | 7.0KB | 549.2KB | +542.2KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net471 | 8.5KB | 546.4KB | +537.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net472 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net48 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net481 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| netcoreapp2.0 | 9.0KB | 513.4KB | +504.4KB | +15.2KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 547.5KB | +539.0KB | +16.2KB | +7.7KB | +13.4KB | +18.4KB | +| net462 | 7.0KB | 552.5KB | +545.5KB | +14.7KB | +8.2KB | +12.4KB | +16.9KB | +| net47 | 7.0KB | 551.7KB | +544.7KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 548.9KB | +540.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net472 | 8.5KB | 546.8KB | +538.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 546.8KB | +538.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 546.8KB | +538.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 515.9KB | +506.9KB | +15.2KB | +8.2KB | +13.9KB | +17.4KB | | netcoreapp2.1 | 9.0KB | 481.0KB | +472.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | | netcoreapp2.2 | 9.0KB | 481.0KB | +472.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | | netcoreapp3.0 | 9.5KB | 470.3KB | +460.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | @@ -46,7 +46,7 @@ | net5.0 | 9.5KB | 414.2KB | +404.7KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | | net6.0 | 10.0KB | 335.8KB | +325.8KB | +17.7KB | +8.7KB | +1.6KB | +4.2KB | | net7.0 | 10.0KB | 287.9KB | +277.9KB | +16.6KB | +6.9KB | +1.1KB | +3.7KB | -| net8.0 | 9.5KB | 243.6KB | +234.1KB | +16.0KB | +811bytes | +1.6KB | +4.2KB | +| net8.0 | 9.5KB | 244.1KB | +234.6KB | +15.5KB | +299bytes | +1.1KB | +3.7KB | | net9.0 | 9.5KB | 167.6KB | +158.1KB | +15.5KB | | +1.1KB | +3.7KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | diff --git a/readme.md b/readme.md index e296f168..755f878c 100644 --- a/readme.md +++ b/readme.md @@ -13,22 +13,22 @@ The package targets `netstandard2.0` and is designed to support the following ru * `uap10` -**API count: 1119** +**API count: 1139** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1038 | -| `net462` | 1038 | -| `net47` | 1037 | -| `net471` | 1036 | -| `net472` | 1032 | -| `net48` | 1032 | -| `net481` | 1032 | -| `netstandard2.0` | 1034 | +| `net461` | 1058 | +| `net462` | 1058 | +| `net47` | 1057 | +| `net471` | 1056 | +| `net472` | 1052 | +| `net48` | 1052 | +| `net481` | 1052 | +| `netstandard2.0` | 1054 | | `netstandard2.1` | 887 | -| `netcoreapp2.0` | 957 | +| `netcoreapp2.0` | 977 | | `netcoreapp2.1` | 898 | | `netcoreapp2.2` | 898 | | `netcoreapp3.0` | 859 | @@ -40,7 +40,7 @@ The package targets `netstandard2.0` and is designed to support the following ru | `net9.0` | 238 | | `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1024 | +| `uap10.0` | 1044 | @@ -96,16 +96,16 @@ 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 | 371.5KB | +363.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.0 | 8.0KB | 373.5KB | +365.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | | netstandard2.1 | 8.5KB | 327.0KB | +318.5KB | +8.5KB | +6.5KB | +8.5KB | +13.5KB | -| net461 | 8.5KB | 370.0KB | +361.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 375.0KB | +368.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net47 | 7.0KB | 375.0KB | +368.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net471 | 8.5KB | 372.5KB | +364.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net472 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net48 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net481 | 8.5KB | 371.5KB | +363.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| netcoreapp2.0 | 9.0KB | 350.5KB | +341.5KB | +7.5KB | +6.5KB | +9.0KB | +13.5KB | +| net461 | 8.5KB | 372.5KB | +364.0KB | +8.5KB | +6.0KB | +8.5KB | +13.0KB | +| net462 | 7.0KB | 377.5KB | +370.5KB | +7.0KB | +6.5KB | +7.5KB | +11.5KB | +| net47 | 7.0KB | 377.0KB | +370.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 374.5KB | +366.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net472 | 8.5KB | 373.5KB | +365.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net48 | 8.5KB | 373.5KB | +365.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| net481 | 8.5KB | 373.5KB | +365.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.0 | 9.0KB | 352.5KB | +343.5KB | +7.5KB | +6.5KB | +9.0KB | +12.0KB | | netcoreapp2.1 | 9.0KB | 330.5KB | +321.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | | netcoreapp2.2 | 9.0KB | 330.5KB | +321.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | | netcoreapp3.0 | 9.5KB | 326.5KB | +317.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | @@ -113,7 +113,7 @@ This project uses features from the newest stable SDK and C# language. As such c | net5.0 | 9.5KB | 288.5KB | +279.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | | net6.0 | 10.0KB | 230.0KB | +220.0KB | +10.0KB | +7.0KB | +1.0KB | +3.5KB | | net7.0 | 10.0KB | 199.0KB | +189.0KB | +9.0KB | +5.5KB | +512bytes | +3.0KB | -| net8.0 | 9.5KB | 168.5KB | +159.0KB | +8.5KB | +512bytes | +1.0KB | +3.5KB | +| net8.0 | 9.5KB | 169.0KB | +159.5KB | +8.0KB | | +512bytes | +3.0KB | | net9.0 | 9.5KB | 116.0KB | +106.5KB | +8.0KB | | +512bytes | +3.0KB | | net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -123,16 +123,16 @@ 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 | 544.9KB | +536.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.0 | 8.0KB | 547.4KB | +539.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | | netstandard2.1 | 8.5KB | 473.8KB | +465.3KB | +16.2KB | +8.2KB | +13.4KB | +18.9KB | -| net461 | 8.5KB | 544.5KB | +536.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 549.5KB | +542.5KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net47 | 7.0KB | 549.2KB | +542.2KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net471 | 8.5KB | 546.4KB | +537.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net472 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net48 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net481 | 8.5KB | 544.3KB | +535.8KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| netcoreapp2.0 | 9.0KB | 513.4KB | +504.4KB | +15.2KB | +8.2KB | +13.9KB | +18.9KB | +| net461 | 8.5KB | 547.5KB | +539.0KB | +16.2KB | +7.7KB | +13.4KB | +18.4KB | +| net462 | 7.0KB | 552.5KB | +545.5KB | +14.7KB | +8.2KB | +12.4KB | +16.9KB | +| net47 | 7.0KB | 551.7KB | +544.7KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 548.9KB | +540.4KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net472 | 8.5KB | 546.8KB | +538.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net48 | 8.5KB | 546.8KB | +538.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| net481 | 8.5KB | 546.8KB | +538.3KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.0 | 9.0KB | 515.9KB | +506.9KB | +15.2KB | +8.2KB | +13.9KB | +17.4KB | | netcoreapp2.1 | 9.0KB | 481.0KB | +472.0KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | | netcoreapp2.2 | 9.0KB | 481.0KB | +472.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | | netcoreapp3.0 | 9.5KB | 470.3KB | +460.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | @@ -140,7 +140,7 @@ This project uses features from the newest stable SDK and C# language. As such c | net5.0 | 9.5KB | 414.2KB | +404.7KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | | net6.0 | 10.0KB | 335.8KB | +325.8KB | +17.7KB | +8.7KB | +1.6KB | +4.2KB | | net7.0 | 10.0KB | 287.9KB | +277.9KB | +16.6KB | +6.9KB | +1.1KB | +3.7KB | -| net8.0 | 9.5KB | 243.6KB | +234.1KB | +16.0KB | +811bytes | +1.6KB | +4.2KB | +| net8.0 | 9.5KB | 244.1KB | +234.6KB | +15.5KB | +299bytes | +1.1KB | +3.7KB | | net9.0 | 9.5KB | 167.6KB | +158.1KB | +15.5KB | | +1.1KB | +3.7KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | @@ -713,12 +713,32 @@ The class `Polyfill` includes the following extension methods: * `float Int32BitsToSingle(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.int32bitstosingle?view=net-11.0) * `int SingleToInt32Bits(float)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.singletoint32bits?view=net-11.0) * `uint SingleToUInt32Bits(float)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.singletouint32bits?view=net-11.0) + * `bool ToBoolean(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toboolean?view=net-11.0#system-bitconverter-toboolean(system-readonlyspan((system-byte)))) + * `char ToChar(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.tochar?view=net-11.0#system-bitconverter-tochar(system-readonlyspan((system-byte)))) + * `double ToDouble(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.todouble?view=net-11.0#system-bitconverter-todouble(system-readonlyspan((system-byte)))) * `Int128 ToInt128(byte[], int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint128?view=net-11.0#system-bitconverter-toint128(system-byte()-system-int32)) * `Int128 ToInt128(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint128?view=net-11.0#system-bitconverter-toint128(system-readonlyspan((system-byte)))) + * `short ToInt16(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint16?view=net-11.0#system-bitconverter-toint16(system-readonlyspan((system-byte)))) + * `int ToInt32(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint32?view=net-11.0#system-bitconverter-toint32(system-readonlyspan((system-byte)))) + * `long ToInt64(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint64?view=net-11.0#system-bitconverter-toint64(system-readonlyspan((system-byte)))) + * `float ToSingle(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.tosingle?view=net-11.0#system-bitconverter-tosingle(system-readonlyspan((system-byte)))) * `UInt128 ToUInt128(byte[], int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint128?view=net-11.0#system-bitconverter-touint128(system-byte()-system-int32)) * `UInt128 ToUInt128(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint128?view=net-11.0#system-bitconverter-touint128(system-readonlyspan((system-byte)))) + * `ushort ToUInt16(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint16?view=net-11.0#system-bitconverter-touint16(system-readonlyspan((system-byte)))) + * `uint ToUInt32(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint32?view=net-11.0#system-bitconverter-touint32(system-readonlyspan((system-byte)))) + * `ulong ToUInt64(ReadOnlySpan)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint64?view=net-11.0#system-bitconverter-touint64(system-readonlyspan((system-byte)))) + * `bool TryWriteBytes(Span, bool)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-boolean)) + * `bool TryWriteBytes(Span, char)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-char)) + * `bool TryWriteBytes(Span, double)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-double)) + * `bool TryWriteBytes(Span, float)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-single)) + * `bool TryWriteBytes(Span, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int32)) * `bool TryWriteBytes(Span, Int128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int128)) + * `bool TryWriteBytes(Span, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int64)) + * `bool TryWriteBytes(Span, short)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int16)) + * `bool TryWriteBytes(Span, uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint32)) * `bool TryWriteBytes(Span, UInt128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint128)) + * `bool TryWriteBytes(Span, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint64)) + * `bool TryWriteBytes(Span, ushort)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint16)) * `float UInt32BitsToSingle(uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.uint32bitstosingle?view=net-11.0) * `double UInt64BitsToDouble(ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.uint64bitstodouble?view=net-11.0) @@ -2513,7 +2533,7 @@ void ObjectDisposedExceptionExample(bool isDisposed) ObjectDisposedException.ThrowIf(isDisposed, typeof(Consume)); } ``` -snippet source | anchor +snippet source | anchor @@ -2532,7 +2552,7 @@ void EnsureExample(Order order, Customer customer, string customerId, string ema this.quantity = Ensure.NotNegativeOrZero(quantity); } ``` -snippet source | anchor +snippet source | anchor diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index 2c57787c..453631fe 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -326,6 +326,36 @@ void Base64Usage() } #endif +#if FeatureMemory + void BitConverter_Span_Methods() + { + var source = new byte[16]; + Span destination = stackalloc byte[16]; + + var boolean = BitConverter.ToBoolean(source.AsSpan()); + var character = BitConverter.ToChar(source.AsSpan()); + var int16 = BitConverter.ToInt16(source.AsSpan()); + var int32 = BitConverter.ToInt32(source.AsSpan()); + var int64 = BitConverter.ToInt64(source.AsSpan()); + var single = BitConverter.ToSingle(source.AsSpan()); + var doubleValue = BitConverter.ToDouble(source.AsSpan()); + var uint16 = BitConverter.ToUInt16(source.AsSpan()); + var uint32 = BitConverter.ToUInt32(source.AsSpan()); + var uint64 = BitConverter.ToUInt64(source.AsSpan()); + + var wroteBoolean = BitConverter.TryWriteBytes(destination, boolean); + var wroteChar = BitConverter.TryWriteBytes(destination, character); + var wroteInt16 = BitConverter.TryWriteBytes(destination, int16); + var wroteInt32 = BitConverter.TryWriteBytes(destination, int32); + var wroteInt64 = BitConverter.TryWriteBytes(destination, int64); + var wroteSingle = BitConverter.TryWriteBytes(destination, single); + var wroteDouble = BitConverter.TryWriteBytes(destination, doubleValue); + var wroteUInt16 = BitConverter.TryWriteBytes(destination, uint16); + var wroteUInt32 = BitConverter.TryWriteBytes(destination, uint32); + var wroteUInt64 = BitConverter.TryWriteBytes(destination, uint64); + } +#endif + #if NET7_0_OR_GREATER void BitConverter_Int128_Methods() { diff --git a/src/Polyfill/Polyfill_BitConverter.cs b/src/Polyfill/Polyfill_BitConverter.cs index 954793f5..c70e3387 100644 --- a/src/Polyfill/Polyfill_BitConverter.cs +++ b/src/Polyfill/Polyfill_BitConverter.cs @@ -127,6 +127,260 @@ public static ulong DoubleToUInt64Bits(double value) #endif +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER + + /// + /// Converts a read-only byte span into a Boolean. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toboolean?view=net-11.0#system-bitconverter-toboolean(system-readonlyspan((system-byte))) + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + + /// + /// Converts a read-only byte span into a Char. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.tochar?view=net-11.0#system-bitconverter-tochar(system-readonlyspan((system-byte))) + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint16?view=net-11.0#system-bitconverter-toint16(system-readonlyspan((system-byte))) + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint32?view=net-11.0#system-bitconverter-toint32(system-readonlyspan((system-byte))) + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.toint64?view=net-11.0#system-bitconverter-toint64(system-readonlyspan((system-byte))) + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.tosingle?view=net-11.0#system-bitconverter-tosingle(system-readonlyspan((system-byte))) + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.todouble?view=net-11.0#system-bitconverter-todouble(system-readonlyspan((system-byte))) + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint16?view=net-11.0#system-bitconverter-touint16(system-readonlyspan((system-byte))) + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint32?view=net-11.0#system-bitconverter-touint32(system-readonlyspan((system-byte))) + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.touint64?view=net-11.0#system-bitconverter-touint64(system-readonlyspan((system-byte))) + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + + /// + /// Converts a Boolean into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-boolean) + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + + /// + /// Converts a Char into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-char) + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + + MemoryMarshal.Write(destination, ref value); + return true; + } + + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int16) + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + + MemoryMarshal.Write(destination, ref value); + return true; + } + + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int32) + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + + MemoryMarshal.Write(destination, ref value); + return true; + } + + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-int64) + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + + MemoryMarshal.Write(destination, ref value); + return true; + } + + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-single) + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + + MemoryMarshal.Write(destination, ref value); + return true; + } + + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-double) + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + + MemoryMarshal.Write(destination, ref value); + return true; + } + + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint16) + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + + MemoryMarshal.Write(destination, ref value); + return true; + } + + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint32) + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + + MemoryMarshal.Write(destination, ref value); + return true; + } + + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.trywritebytes?view=net-11.0#system-bitconverter-trywritebytes(system-span((system-byte))-system-uint64) + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + + MemoryMarshal.Write(destination, ref value); + return true; + } + +#endif + #if NET7_0_OR_GREATER && !NET9_0_OR_GREATER /// @@ -282,6 +536,20 @@ static void GuardSixteenBytes(ReadOnlySpan value) #endif +#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER + + // the BCL span overloads reject a short span with a plain ArgumentOutOfRangeException + // naming value, rather than the ArgumentException the array overloads use + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + } + +#endif + } #endif diff --git a/src/Split/net461/Polyfill_BitConverter.cs b/src/Split/net461/Polyfill_BitConverter.cs index 9cb85b28..5beebc7f 100644 --- a/src/Split/net461/Polyfill_BitConverter.cs +++ b/src/Split/net461/Polyfill_BitConverter.cs @@ -85,6 +85,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Split/net462/Polyfill_BitConverter.cs b/src/Split/net462/Polyfill_BitConverter.cs index 9cb85b28..5beebc7f 100644 --- a/src/Split/net462/Polyfill_BitConverter.cs +++ b/src/Split/net462/Polyfill_BitConverter.cs @@ -85,6 +85,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Split/net47/Polyfill_BitConverter.cs b/src/Split/net47/Polyfill_BitConverter.cs index 9cb85b28..5beebc7f 100644 --- a/src/Split/net47/Polyfill_BitConverter.cs +++ b/src/Split/net47/Polyfill_BitConverter.cs @@ -85,6 +85,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Split/net471/Polyfill_BitConverter.cs b/src/Split/net471/Polyfill_BitConverter.cs index 9cb85b28..5beebc7f 100644 --- a/src/Split/net471/Polyfill_BitConverter.cs +++ b/src/Split/net471/Polyfill_BitConverter.cs @@ -85,6 +85,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Split/net472/Polyfill_BitConverter.cs b/src/Split/net472/Polyfill_BitConverter.cs index 9cb85b28..5beebc7f 100644 --- a/src/Split/net472/Polyfill_BitConverter.cs +++ b/src/Split/net472/Polyfill_BitConverter.cs @@ -85,6 +85,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Split/net48/Polyfill_BitConverter.cs b/src/Split/net48/Polyfill_BitConverter.cs index 9cb85b28..5beebc7f 100644 --- a/src/Split/net48/Polyfill_BitConverter.cs +++ b/src/Split/net48/Polyfill_BitConverter.cs @@ -85,6 +85,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Split/net481/Polyfill_BitConverter.cs b/src/Split/net481/Polyfill_BitConverter.cs index 9cb85b28..5beebc7f 100644 --- a/src/Split/net481/Polyfill_BitConverter.cs +++ b/src/Split/net481/Polyfill_BitConverter.cs @@ -85,6 +85,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Split/netcoreapp2.0/Polyfill_BitConverter.cs b/src/Split/netcoreapp2.0/Polyfill_BitConverter.cs index f05be0bc..fd4cd7b8 100644 --- a/src/Split/netcoreapp2.0/Polyfill_BitConverter.cs +++ b/src/Split/netcoreapp2.0/Polyfill_BitConverter.cs @@ -41,6 +41,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Split/netstandard2.0/Polyfill_BitConverter.cs b/src/Split/netstandard2.0/Polyfill_BitConverter.cs index 9cb85b28..5beebc7f 100644 --- a/src/Split/netstandard2.0/Polyfill_BitConverter.cs +++ b/src/Split/netstandard2.0/Polyfill_BitConverter.cs @@ -85,6 +85,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Split/uap10.0/Polyfill_BitConverter.cs b/src/Split/uap10.0/Polyfill_BitConverter.cs index 9cb85b28..5beebc7f 100644 --- a/src/Split/uap10.0/Polyfill_BitConverter.cs +++ b/src/Split/uap10.0/Polyfill_BitConverter.cs @@ -85,6 +85,213 @@ public static ulong DoubleToUInt64Bits(double value) { return (ulong)BitConverter.DoubleToInt64Bits(value); } + /// + /// Converts a read-only byte span into a Boolean. + /// + public static bool ToBoolean(ReadOnlySpan value) + { + GuardSpan(value, 1); + return value[0] != 0; + } + /// + /// Converts a read-only byte span into a Char. + /// + public static char ToChar(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit signed integer. + /// + public static short ToInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit signed integer. + /// + public static int ToInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit signed integer. + /// + public static long ToInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a single-precision floating point number. + /// + public static float ToSingle(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a double-precision floating point number. + /// + public static double ToDouble(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 16-bit unsigned integer. + /// + public static ushort ToUInt16(ReadOnlySpan value) + { + GuardSpan(value, 2); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 32-bit unsigned integer. + /// + public static uint ToUInt32(ReadOnlySpan value) + { + GuardSpan(value, 4); + return MemoryMarshal.Read(value); + } + /// + /// Converts a read-only byte span into a 64-bit unsigned integer. + /// + public static ulong ToUInt64(ReadOnlySpan value) + { + GuardSpan(value, 8); + return MemoryMarshal.Read(value); + } + /// + /// Converts a Boolean into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, bool value) + { + if (destination.Length < 1) + { + return false; + } + destination[0] = value ? (byte) 1 : (byte) 0; + return true; + } + /// + /// Converts a Char into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, char value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, short value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, int value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit signed integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, long value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a single-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, float value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a double-precision floating point number into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, double value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 16-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ushort value) + { + if (destination.Length < 2) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 32-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, uint value) + { + if (destination.Length < 4) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + /// + /// Converts a 64-bit unsigned integer into a span of bytes. + /// + public static bool TryWriteBytes(Span destination, ulong value) + { + if (destination.Length < 8) + { + return false; + } + MemoryMarshal.Write(destination, ref value); + return true; + } + } + static void GuardSpan(ReadOnlySpan value, int size) + { + if (value.Length < size) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } } } #endif diff --git a/src/Tests/BitConverterSpanTests.cs b/src/Tests/BitConverterSpanTests.cs new file mode 100644 index 00000000..1b495f1a --- /dev/null +++ b/src/Tests/BitConverterSpanTests.cs @@ -0,0 +1,177 @@ +#if FeatureMemory + +// Runs on every target, so netstandard2.1/netcoreapp2.1 and later validate the BCL and +// netstandard2.0, netcoreapp2.0 and .NET Framework validate the polyfill. +public class BitConverterSpanTests +{ + static byte[] source = BuildSource(); + + static byte[] BuildSource() + { + var bytes = new byte[24]; + for (var i = 0; i < bytes.Length; i++) + { + bytes[i] = (byte) (i + 1); + } + + return bytes; + } + + [Test] + public async Task ReadsMatchTheArrayOverloads() + { + // an odd offset, so an implementation that assumes alignment would fault or misread + foreach (var offset in new[] { 0, 1, 3, 5, 8 }) + { + await Assert.That(ReadBoolean(offset)).IsEqualTo(BitConverter.ToBoolean(source, offset)); + await Assert.That(ReadChar(offset)).IsEqualTo(BitConverter.ToChar(source, offset)); + await Assert.That(ReadInt16(offset)).IsEqualTo(BitConverter.ToInt16(source, offset)); + await Assert.That(ReadInt32(offset)).IsEqualTo(BitConverter.ToInt32(source, offset)); + await Assert.That(ReadInt64(offset)).IsEqualTo(BitConverter.ToInt64(source, offset)); + await Assert.That(ReadSingle(offset)).IsEqualTo(BitConverter.ToSingle(source, offset)); + await Assert.That(ReadDouble(offset)).IsEqualTo(BitConverter.ToDouble(source, offset)); + await Assert.That(ReadUInt16(offset)).IsEqualTo(BitConverter.ToUInt16(source, offset)); + await Assert.That(ReadUInt32(offset)).IsEqualTo(BitConverter.ToUInt32(source, offset)); + await Assert.That(ReadUInt64(offset)).IsEqualTo(BitConverter.ToUInt64(source, offset)); + } + } + + [Test] + public async Task ToBoolean_TreatsAnyNonZeroAsTrue() + { + await Assert.That(ReadBooleanOf(0)).IsFalse(); + await Assert.That(ReadBooleanOf(1)).IsTrue(); + await Assert.That(ReadBooleanOf(2)).IsTrue(); + await Assert.That(ReadBooleanOf(255)).IsTrue(); + } + + [Test] + public async Task ShortSpanThrows() + { + await Assert.That(Capture(() => ReadBoolean(source, 0))).IsEqualTo("ArgumentOutOfRangeException:value"); + await Assert.That(Capture(() => ReadChar(source, 1))).IsEqualTo("ArgumentOutOfRangeException:value"); + await Assert.That(Capture(() => ReadInt16(source, 1))).IsEqualTo("ArgumentOutOfRangeException:value"); + await Assert.That(Capture(() => ReadInt32(source, 3))).IsEqualTo("ArgumentOutOfRangeException:value"); + await Assert.That(Capture(() => ReadInt64(source, 7))).IsEqualTo("ArgumentOutOfRangeException:value"); + await Assert.That(Capture(() => ReadSingle(source, 3))).IsEqualTo("ArgumentOutOfRangeException:value"); + await Assert.That(Capture(() => ReadDouble(source, 7))).IsEqualTo("ArgumentOutOfRangeException:value"); + await Assert.That(Capture(() => ReadUInt16(source, 1))).IsEqualTo("ArgumentOutOfRangeException:value"); + await Assert.That(Capture(() => ReadUInt32(source, 3))).IsEqualTo("ArgumentOutOfRangeException:value"); + await Assert.That(Capture(() => ReadUInt64(source, 7))).IsEqualTo("ArgumentOutOfRangeException:value"); + + // exactly enough is accepted + await Assert.That(Capture(() => ReadInt32(source, 4))).IsEqualTo("none"); + await Assert.That(Capture(() => ReadInt64(source, 8))).IsEqualTo("none"); + } + + [Test] + public async Task TryWriteBytes_MatchesGetBytesAndSizing() + { + await Check(1, BitConverter.GetBytes(true), (buffer, _) => BitConverter.TryWriteBytes(buffer, true)); + await Check(2, BitConverter.GetBytes('A'), (buffer, _) => BitConverter.TryWriteBytes(buffer, 'A')); + await Check(2, BitConverter.GetBytes((short) 0x1234), (buffer, _) => BitConverter.TryWriteBytes(buffer, (short) 0x1234)); + await Check(4, BitConverter.GetBytes(0x12345678), (buffer, _) => BitConverter.TryWriteBytes(buffer, 0x12345678)); + await Check(8, BitConverter.GetBytes(0x1122334455667788L), (buffer, _) => BitConverter.TryWriteBytes(buffer, 0x1122334455667788L)); + await Check(4, BitConverter.GetBytes(1.5f), (buffer, _) => BitConverter.TryWriteBytes(buffer, 1.5f)); + await Check(8, BitConverter.GetBytes(1.5d), (buffer, _) => BitConverter.TryWriteBytes(buffer, 1.5d)); + await Check(2, BitConverter.GetBytes((ushort) 0xABCD), (buffer, _) => BitConverter.TryWriteBytes(buffer, (ushort) 0xABCD)); + await Check(4, BitConverter.GetBytes(0xDEADBEEFu), (buffer, _) => BitConverter.TryWriteBytes(buffer, 0xDEADBEEFu)); + await Check(8, BitConverter.GetBytes(0xFEEDFACECAFEBEEFuL), (buffer, _) => BitConverter.TryWriteBytes(buffer, 0xFEEDFACECAFEBEEFuL)); + } + + static async Task Check(int width, byte[] expected, Func write) + { + // one byte short: refused, and the destination is left alone + var small = Filled(width - 1); + var smallOk = write(small, 0); + await Assert.That(smallOk).IsFalse(); + await Assert.That(AllFill(small)).IsTrue(); + + var exact = Filled(width); + var exactOk = write(exact, 0); + await Assert.That(exactOk).IsTrue(); + await Assert.That(Hex(exact)).IsEqualTo(Hex(expected)); + + // a larger destination is written only up to the width of the value + var larger = Filled(width + 1); + var largerOk = write(larger, 0); + await Assert.That(largerOk).IsTrue(); + await Assert.That(Hex(larger)).IsEqualTo(Hex(expected) + "aa"); + } + + static byte[] Filled(int size) + { + var buffer = new byte[size]; + for (var i = 0; i < size; i++) + { + buffer[i] = 0xAA; + } + + return buffer; + } + + static bool AllFill(byte[] buffer) + { + foreach (var b in buffer) + { + if (b != 0xAA) + { + return false; + } + } + + return true; + } + + // the spans stay inside these helpers, so nothing ref struct shaped lives across an await + static bool ReadBoolean(int offset) => BitConverter.ToBoolean(source.AsSpan(offset)); + static char ReadChar(int offset) => BitConverter.ToChar(source.AsSpan(offset)); + static short ReadInt16(int offset) => BitConverter.ToInt16(source.AsSpan(offset)); + static int ReadInt32(int offset) => BitConverter.ToInt32(source.AsSpan(offset)); + static long ReadInt64(int offset) => BitConverter.ToInt64(source.AsSpan(offset)); + static float ReadSingle(int offset) => BitConverter.ToSingle(source.AsSpan(offset)); + static double ReadDouble(int offset) => BitConverter.ToDouble(source.AsSpan(offset)); + static ushort ReadUInt16(int offset) => BitConverter.ToUInt16(source.AsSpan(offset)); + static uint ReadUInt32(int offset) => BitConverter.ToUInt32(source.AsSpan(offset)); + static ulong ReadUInt64(int offset) => BitConverter.ToUInt64(source.AsSpan(offset)); + + // read from the tail of the buffer so that only `length` bytes remain + static bool ReadBoolean(byte[] buffer, int length) => BitConverter.ToBoolean(buffer.AsSpan(buffer.Length - length, length)); + static char ReadChar(byte[] buffer, int length) => BitConverter.ToChar(buffer.AsSpan(buffer.Length - length, length)); + static short ReadInt16(byte[] buffer, int length) => BitConverter.ToInt16(buffer.AsSpan(buffer.Length - length, length)); + static int ReadInt32(byte[] buffer, int length) => BitConverter.ToInt32(buffer.AsSpan(buffer.Length - length, length)); + static long ReadInt64(byte[] buffer, int length) => BitConverter.ToInt64(buffer.AsSpan(buffer.Length - length, length)); + static float ReadSingle(byte[] buffer, int length) => BitConverter.ToSingle(buffer.AsSpan(buffer.Length - length, length)); + static double ReadDouble(byte[] buffer, int length) => BitConverter.ToDouble(buffer.AsSpan(buffer.Length - length, length)); + static ushort ReadUInt16(byte[] buffer, int length) => BitConverter.ToUInt16(buffer.AsSpan(buffer.Length - length, length)); + static uint ReadUInt32(byte[] buffer, int length) => BitConverter.ToUInt32(buffer.AsSpan(buffer.Length - length, length)); + static ulong ReadUInt64(byte[] buffer, int length) => BitConverter.ToUInt64(buffer.AsSpan(buffer.Length - length, length)); + + static bool ReadBooleanOf(byte value) => BitConverter.ToBoolean(new[] { value }.AsSpan()); + + static string Hex(byte[] bytes) + { + var builder = new System.Text.StringBuilder(bytes.Length * 2); + foreach (var b in bytes) + { + builder.Append(b.ToString("x2")); + } + + return builder.ToString(); + } + + static string Capture(Action action) + { + try + { + action(); + return "none"; + } + catch (ArgumentException exception) + { + return $"{exception.GetType().Name}:{exception.ParamName}"; + } + } +} + +#endif