Jcd.BitManipulation
2.4.0
Prefix Reserved
See the version list below for details.
dotnet add package Jcd.BitManipulation --version 2.4.0
NuGet\Install-Package Jcd.BitManipulation -Version 2.4.0
<PackageReference Include="Jcd.BitManipulation" Version="2.4.0" />
paket add Jcd.BitManipulation --version 2.4.0
#r "nuget: Jcd.BitManipulation, 2.4.0"
// Install Jcd.BitManipulation as a Cake Addin #addin nuget:?package=Jcd.BitManipulation&version=2.4.0 // Install Jcd.BitManipulation as a Cake Tool #tool nuget:?package=Jcd.BitManipulation&version=2.4.0
Jcd.BitManipulation
A .Net Standard 2.0 library adding syntactic sugar to your bit manipulation experience.
Examples
ushort data = 0b0000000000000000;
// turn on all the bits
data = data.SetBits(0, 16); // value is now 0b1111111111111111
// this is the equivalent as above
data = data.SetBits();
// Clear the middle 4 bits.
data = data.ClearBits(4, 8); // value is now 0b1111000000001111
// Toggle all the bits.
data = data.ToggleBits(); // value is now 0b0000111111110000
var finalData = data;
// read the upper byte
var upperByte = (byte) data.ReadBits(8, 8); // upperByte is now 0b00001111
// write 0b1011 into the upper nybble
upperByte = upperByte.StoreBits(0b1011, 4, 4); // upperByte is now 0b10111111
// chaining operations, the same steps and end results
data.ClearBits();
data = data.SetBits(0, 16) // value is now 0b1111111111111111
.SetBits() // this is the equivalent as above
.ClearBits(4, 8) // value is now 0b1111000000001111
.ToggleBits(); // value is now 0b0000111111110000
upperByte = ((byte) data.ReadBits(8, 8)) // extract the upper byte (0b00001111)
.StoreBits(0b1011, 4, 4); // store the value in the upper 4 bits, now upperByte is now 0b10111111
// finalData 0b0000111111110000
var beByte0 = finalData.ReadByte(0, Endian.Big); // 00001111
var leByte0 = finalData.ReadByte(0, Endian.Little); // 11110000
var mutatedData = finalData
.StoreByte(0b10111111, 0, Endian.Big)
.StoreByte(0b01010101, 0, Endian.Little ); // lower byte is now 0b01010101
; // mutatedData is now 0b1011111101010101
var beBa = mutatedData.ToByteArray(Endian.Big); // beBa=[0b10111111, 0b01010101]
var leBa = mutatedData.ToByteArray(Endian.Little); // leBa=[0b01010101, 0b10111111]
var leBaToUInt16Le = leBa.ToUInt16(Endian.Little); // leBaToUInt16Le = 0b1011111101010101
var leBaToUInt16Be = leBa.ToUInt16(Endian.Big); // leBaToUInt16Le = 0b0101010110111111
Performance Notes
If you read the code you'll notice a fair number of abstractions at play. These don't have a significant impact on release mode performance. In fact running on at roughly 3.5GHz to 4.0Ghz (my CPU auto-scales performance based on load.) doing the 6 chained operations at the end of the example below, but on the loop index instead of just zero, 10+e9 times completes in roughly 830-890ms in Release mode.
To see how it performs on your system run the code in the Main function of the examples app.
1,000,000,000 iterations with 6 operations per iteration took 868.58 ms for an average of 14.5 ns per operation.
Even more extensive performance testing is underway with version 3.0. Read below for details.
Upcoming Changes
2.x Summary
In the rush to get 2.x out the door I missed including Endianness with some of the extension methods and related unit tests. I've found a number of these in my work on 3.0. Many of these fixes and improvements have already been back ported to 2.x from 3.0. Others will follow as I find them and will be released in 2.x well ahead of the 3.0 release.
You have my sincerest apologies for these oversights.
3.0 Summary
As you've guessed, I'm actively working on v3.0. Some big changes will hit with that release. If you have any wants from this library please add an issue with your request.
The biggest changes are focused on completeness, simplifying the API surface/reducing volume of code, and and keeping acceptable performance<sup>1</sup>.
Below is are list of currently planned changes.
- One notable exception is a known performance hit caused by simplifying the API surface. It's described in detail below.
3.0 Planned breaking changes
- I've found myself strongly disliking the proliferation of types. All of the type specific
BitIndexer
andByteIndexer
types will be replaced with four types:BigEndianByteIndexer
,LittleEndianByteIndexer
,BigEndianBitIndexer
,LittleEndianBitIndexer
. - The old indexer types will be deprecated in an upcoming release of 2.x, shortly before 3.0 is released. If you're using them keep in mind that you will need to change over. Consider scoping out the change once the first prerelease of 3.0 is published.
- The
IByteIndexer
andIBitIndexer
interfaces will go away. I'm not using them internally and using them causes boxing allocations. Frequent boxing allocations can consume a lot of heap memory. The intended use case for the indexers was at the point where bit/byte level access is required (i.e. stack allocated). - For the same reasons as above none of the indexer types will derive from IEnumerable. Instead explicit
conversion operators will be provided to and from
Span
s andArray
s. - To the end of enforcing the indexers stack allocated and used in place, the bit and byte indexer types will become
ref structs
. This carries some implications such as making them non-viable as fields and async return types. That's in keeping with the original intent and a desirable change, from my view. If you need them to remain heap allocatable, add an issue or open a dialog with me about your needs.
3.0 New Features / Improvements
- Byte and bit level indexing of IEEE.754 floating point types:
double
andfloat
will be directly provided in 3.0. This is currently possible if one first uses BitConverter to convert to the same sized integer type. The support for these types will be much more comprehensive than that in 2.0. (e.g..SetBits(this float,...)
will return afloat
with the specified bits altered) - Performance improvements via
AggressiveInlining
. In many places in 1.x and 2.x,AggressiveInlining
is missing. Using this consistently has halved and quartered the run time of some of the extension methods in the 3.0 branch. - Performance benchmarks! I will include both the most recent performance benchmark runs as well as the benchmark
application in 3.0. In running this I confirmed that the slight performance boost I got going with the
type specific byte indexers, was not worth the hassle of the volume of code it requires for a .Net standard 2.0
library. To illustrate (in the 3.0 branch, using my machine):
- Using
BitConverter
to create an array from any int of any size and reverse the array (big endian on Intel processors) is 4ns on .Net 8.0 and 25ns in .Net Framework 4.8.1. - Using
BigEndianByteIndexerUInt64
takes rougly 6ns on both .Net 8.0 and 10ns on .Net Framework 4.8.1, - Using
BigEndianByteIndexerUInt16
takes roughly 3ns on both .Net 8.0 and 10ns on .Net Framework 4.8.1, - The replacement indexers need more logic as they are handling multiple bit sizes.
BigEndianByteIndexer
runs at roughly 8ns per conversion of theUInt64
to an array of bytes. - This loss of performance to accomodate easier to maintain code is acceptable for two reasons:
- In the worst case there's 6ns difference from
BitConverter
, and 5ns from the baseline implementation. 8ns per operation at 4GHz is still sufficiently fast for most purposes. - The target audience for this library are people who want more readable code. This sometimes comes at a slight performance hit. I believe a loss of performance of 6ns per conversion to a big endian array on an Intel processor is performant enough for the target audience.
- If you really need as fast of performance as possible in converting integer to and from arrays, you may need to
roll your own solution tailored to the hardware you're running on. Even
BitConverter
can be less performant than hand-rolled solutions for 16 bit integers on my machine.
- In the worst case there's 6ns difference from
- Using
- Improved documentation. I will improve the documentation on how to properly use the extension methods as well as their underlying types. One area currently lacking is on how to expect a big endian conversion from a a byte array to behave when the array is smaller than the destination type. (fills from position 0)
- ReSharper auto-formatting updates. Some of the code just looks silly with the settings used in 2.x (main) branch. I will update these settings to remove that odd formatting. (This mostly impacts data driven unit tests making it harder than necessary to comprehend what's happening)
3.0 Tentative Feature/Addition
- I will investigate .Net 7.0 generic math as a possible way of providing a truly generic implementation. (i.e. BigEndianByteIndexer<int> for example.) If this approach proves performant enough, I'll create a .net 7.0 version of this library with that support built in. The .Net Standard 2.0 compatible indexers will still be provided.
Change Log
Version 2.4 Released
- Added
ReadByte
extension methods forIByteIndexer
derived types. - Updated all
Slice
methods to prevent possible array index out of bounds. - Added
AggressiveInlining
hints to help boost performance.
Version 2.3 Released
- Added an Endian parameter to
StoreByte
. - Improved efficiency of
ReadByte
calls. - Fixed numerous copy and paste errors in the documentation.
- Added examples of using
StoreByte
andToByteArray
.
Version 2.2 Released
- Added
AreAnyBitsSet
extensions methods for all intrinsic integer types. - Added
AreAllBitsSet
extensions methods for all intrinsic integer types. - Added
AreNoBitsSet
extensions methods for all intrinsic integer types.
Version 2.1 Released
- Added
GetBytes
andGetByte
extension methods for intrinsic data types. - Added
StoreBytes
andStoreByte
extension methods for intrinsic data types. - Added
LittleEndianXXXByteIndexer
classes. - Added
BigEndianXXXByteIndexer
classes.
Version 2.0 Released
Breaking Changes from v1.x
- Renamed
BitMask.CreateRange
toBitMask.FromRange
. - Renamed
BitMask.CreateSingleBit
toBitMask.FromSingleBit
. - All
StoreBit
overloads had bit and offset parameters swapped for semantic parity withStoreBits
- Extension methods:
ClearBits
,SetBits
,StoreBits
,ToggleBits
,ClearBit
,SetBit
,StoreBit
, andToggleBit
no longer takeref
parameters. Now they return the modified result instead of modifying the original variable. This gives a net benefit of being able to use fluent syntax to chain together bit manipulation operations.
Build, Code Stats and Nuget
API Documentation
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Jcd.Netstandard20.Shim (>= 1.0.1)
- System.Memory (>= 4.5.5)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Jcd.BitManipulation:
Package | Downloads |
---|---|
Jcd.RichEnumerations
A .netstandard 2.0 library that provides DDD-style rich enumeration base types for both _plain old classes_ and `record` types. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
3.0.125 | 110 | 9/13/2024 |
3.0.124 | 261 | 8/1/2024 |
3.0.121 | 248 | 4/14/2024 |
3.0.108-alpha | 157 | 4/14/2024 |
3.0.105-alpha | 83 | 4/13/2024 |
3.0.102-alpha | 111 | 4/7/2024 |
3.0.90-alpha | 102 | 4/6/2024 |
3.0.87-alpha | 96 | 4/2/2024 |
3.0.55-alpha | 68 | 4/1/2024 |
3.0.32-alpha | 99 | 3/31/2024 |
3.0.30-alpha | 83 | 3/27/2024 |
2.4.33 | 108 | 3/31/2024 |
2.4.11 | 119 | 3/23/2024 |
2.4.0 | 118 | 3/23/2024 |
2.3.0 | 109 | 3/23/2024 |
2.2.0 | 122 | 3/17/2024 |
2.1.0 | 122 | 3/14/2024 |
2.0.5 | 241 | 3/23/2023 |
1.0.30 | 418 | 10/11/2021 |
1.0.28 | 330 | 10/11/2021 |
Added bit testing extensions.