Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / Visual Basic

Almost BitFields in .NET

Rate me:
Please Sign up or sign in to vote.
3.89/5 (7 votes)
4 May 2017CPOL5 min read 26.2K   107   1   26
How to implement a quasi-bitfield in .NET

Introduction

If you have a background in C or C++ and have moved or dabbled in .NET, you may have noticed that Bitfields are missing from .NET. You can visit the suggestion site for future additions to .NET but there doesn't seem to be enough of a draw to get this implemented. In this article, I'm going to show how to build a Bitfield like structure.

Background

Those of you who have never used a Bitfield may be wondering why this might be important. A Bitfield will allow you to pack a bunch of values into one single integer. Many communication and recording structures use bitfields to group a bunch of settings into one value; a good example is the DCB structure in windows for setting up and configuring serial ports.

Typically in C, you could build a structure looking like this:

C++
//bit field box properties
struct Box{
 unsigned int opaque     :1;
 unsigned int fill_color :4;
 unsigned int something  :4; // remaining bits after this are unused
}box1;

//access the fields like this
box1.fill_color=2;
box1.opaque=1;

What's great about that is it makes it feel like an object to manipulate instead of A LOT of constants, masks and spaghetti code managing all the little bits for setting, flipping and clearing. The same thing above could look like this when doing bit twiddling (shown in VB).

VB.NET
Dim box1 as int32
Dim Const OPAQUE as int32 = &b1
Dim Const FILL_COLOR as int32=&b11110
Dim Const SOMETHING as int32=&b111100000

'set the fill color with clearing the bits first
box1=box1 and not FILL_COLOR
box1 = box1 or 2<<1

'set the opaque flag
box1=box1 or OPAQUE

This example is not bad (yet) but after a couple hundred lines of code, you start losing the meaning for OPAQUE; does it go with the box variables or does it also apply to the circle variables? Did I set that bit? Do I encapsulate the value into a whole class to manage dealing with the constants? How many spaces do I shift over for each mask, and do I make constants for those too?

This made me wish for Bitfields so many times it was crazy.

Using the Code

Since I can't have built in support for bitfields, I'll have to roll out my own using generics:

VB.NET
<CLSCompliant(False)> Public Structure BitField16_
(Of enumeration As {Structure, IComparable, IConvertible, IFormattable})
    Public Value As UInt16

    Public Sub New(initial As UInt16)
        Value = initial
    End Sub

    'gets or sets the bits depending on the enum passed
    Public Property [field](v As enumeration) As UInt16
        Get
            Dim i As Integer = [Enum].ToObject(GetType(enumeration), v)
            Return (Value And i) >> shifted(i)
        End Get
        Set(newvalue As UInt16)
            Dim i As Integer = [Enum].ToObject(GetType(enumeration), v)
            Value = Value And Not (i) 'clear any old bits
            Value = Value Or (i And (newvalue << shifted(i))) 'set any new bits
        End Set
    End Property

    'finds the first shifed bit and returns the offset that it sits at
    Private Function shifted(mask As Integer) As Byte
        Dim v As Integer = (mask And -mask) 'removes all but the LSB
        Dim c As Integer = 0 'counter
        Do Until v = 1
            v = v >> 1 'keep shifting right until the value is 1
            c += 1
        Loop
        Return c
    End Function

End Structure

I used a structure instead of a class as it will keep it on the stack and easily tossed once I'm done with the variable. The second thing you might notice is the "Of enumeration" in the structure declaration; that's right we are going to pass a Enumeration to this structure to define the fields of the bitfield.

VB.NET
<Flags>Public Enum testEnum
    bits1 = &B11
    bits2 = &B11100
    bits3 = &B100000
    bits4 = &B11000000
End Enum

Instead of letting the enum go from 0,1,2,3... internally, we set the value using "&B" binary constants so we can see the bits each mask will handle. (somedays, I wish VB would let me write "&B0000 0011" to make things line up pretty) so you can see that "bits1" can take up to two bits for a maximum value of 3 where "bits3" only has one and can only take 0 or 1. Other than that, you can make your own enum however you want, just be sure not to go over the 16 bits (or 32,64 if you change it up a bit) in your masks.

The "shifted" function will return how many positions a mask is shifted to the left, this will help decipher the value when getting or setting with the unsigned integer. Usage becomes pretty simple now:

VB.NET
Dim t as BitField16(Of testEnum)

t.field(testEnum.bits3) = 5 'can only store 0 or 1 the remaining bits get ignored
t.field(testEnum.bits2 = 5 'can store 3 bits worth so &b101 will fit just fine in that field

Yes, it's a little bit more wordy than the "C" style bitmask, but it's a lot cleaner than a bunch of constants and other mess. and the internal value can be saved streamed or whatever is needed.

Points of Interest

I chose the UInt16 (Ushort) because I handle a bunch of serialport data in my day to day work and most remote devices I work with only handle up to 16 bit data; so feel free to change it up to handle UInt32 or UInt64 type data. If you want to use any signed types for the bitfield like a Int32, just be sure your Enum masks don't use the MSB as the .NET runtime will throw a fit.

Never try to set a negative value to one of the fields as you will lose data, Bitfields can only handle positive data.

This code is not super robust at the moment as it should throw a exception if an Enum has a mask with 0 in one of the fields or if some other valuetype is passed to the generic other than an enum will cause issues, but I needed something lightweight and fairly quick; the "shifted" function I wish I could trim off cycles to make it leaner but I'm sure using higher end math would add more cycles. If you have a suggestion for the "shifted" function, I'll be happy to hear it.

The "[Enum].ToObject(GetType(enumeration), v)" casting and reflection are also a bit of performance hit, but the trade off for cleaner code I feel is worth it; as the alternative is to write specialized structures to handle each type bitfield I deal with and handle all the bit twiddling there.

In the sample code, I've also included type case operators to get/set a UInt16 directly to the structure without having to deal with the Value field directly.

Image 1

In the attached project, it will also have 4 speed tests on different types of tests shown in system ticks running on my i7 Thinkpad.

  1. The first speed test is the generic (the slowest of the bunch) but most flexible and least amount of future maintenance.
  2. The second is the non-generic test with a known Enum, much quicker, but now requires a structure per Bitfield.
  3. The third is a full structure with each field written out, quickest yet, cleaner on client code, a lot more future maintenance if new fields are needed, largest in code size.
  4. The last speed test is a base line for working with a UInt16 directly, this is the quickest because no calls to other function are being put on the stack but the most fragle of the bunch as the client code has to handle and maintain all the bits.

You can also see in all 4 cases that the size required is still only 2 bytes.

History

  • 2nd May, 2017: Initial write up
  • 3rd May, 2017: Added source code to compare speed tests
  • 4th May, 2017: Added the "Flags" attribute to the Enum, thanks BillWoodruff for pointing that out

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Van Doren Sales
United States United States
I've been a professional developer since the late 90's and got hired on with Doubl-Kold in may of 2000 through February of 2018. I have since moved on to Van Doren Sales for new challenges. I have a few core languages under my belt, but D-lang has gotten my interest lately.

I currently have interest into more embedded systems, as they continue to fascinate me more than any other technologies currently.

The majority of my free time is spent on my wife and kids, and numerous other hobbies.

Comments and Discussions

 
QuestionEndian-ness Pin
Budsy5-May-17 10:36
Budsy5-May-17 10:36 
AnswerRe: Endian-ness Pin
Matt McGuire8-May-17 5:29
professionalMatt McGuire8-May-17 5:29 
QuestionSee: C# Numeric literal syntax improvements Pin
Izhar A.5-May-17 5:32
Izhar A.5-May-17 5:32 
AnswerRe: See: C# Numeric literal syntax improvements Pin
Matt McGuire8-May-17 5:48
professionalMatt McGuire8-May-17 5:48 
GeneralMy vote of 4 Pin
BillWoodruff4-May-17 9:36
professionalBillWoodruff4-May-17 9:36 
QuestionBitfields.... maybe Pin
Budsy3-May-17 19:46
Budsy3-May-17 19:46 
AnswerRe: Bitfields.... maybe Pin
Matt McGuire4-May-17 8:49
professionalMatt McGuire4-May-17 8:49 
QuestionSee: Pin
Member 130801543-May-17 9:51
Member 130801543-May-17 9:51 
AnswerRe: See: Pin
Matt McGuire3-May-17 11:11
professionalMatt McGuire3-May-17 11:11 
BugDownload link not working Pin
Bryian Tan3-May-17 8:45
professionalBryian Tan3-May-17 8:45 
GeneralRe: Download link not working Pin
Matt McGuire3-May-17 11:01
professionalMatt McGuire3-May-17 11:01 
GeneralRe: Download link not working Pin
Bryian Tan3-May-17 11:37
professionalBryian Tan3-May-17 11:37 
GeneralMy vote of 2 Pin
BillWoodruff3-May-17 8:30
professionalBillWoodruff3-May-17 8:30 
GeneralRe: My vote of 2 Pin
Matt McGuire3-May-17 11:46
professionalMatt McGuire3-May-17 11:46 
GeneralRe: My vote of 2 Pin
BillWoodruff4-May-17 2:01
professionalBillWoodruff4-May-17 2:01 
GeneralRe: My vote of 2 Pin
Matt McGuire4-May-17 4:47
professionalMatt McGuire4-May-17 4:47 
GeneralRe: My vote of 2 Pin
BillWoodruff4-May-17 10:34
professionalBillWoodruff4-May-17 10:34 
GeneralRe: My vote of 2 Pin
Andre_Prellwitz11-May-17 11:11
Andre_Prellwitz11-May-17 11:11 
GeneralRe: My vote of 2 Pin
Andre_Prellwitz11-May-17 11:19
Andre_Prellwitz11-May-17 11:19 
GeneralRe: My vote of 2 Pin
Andre_Prellwitz11-May-17 11:08
Andre_Prellwitz11-May-17 11:08 
QuestionBitArray & BitVector32 Pin
K.Ostrouska3-May-17 6:50
K.Ostrouska3-May-17 6:50 
AnswerRe: BitArray & BitVector32 Pin
Matt McGuire3-May-17 12:04
professionalMatt McGuire3-May-17 12:04 
QuestionBleah! Pin
Member 32560313-May-17 2:27
professionalMember 32560313-May-17 2:27 
AnswerRe: Bleah! Pin
Matt McGuire3-May-17 12:42
professionalMatt McGuire3-May-17 12:42 
I'm not going to be drug into another language war, it's like arguing what's better honey or jam on toast, both are good.

Since Vb.NET is a first class language in .NET and for the most part shares all the same capabilities of C#; I am curious and I have to ask why do you feel it's disgusting? Is it a technical reason or just a personal one?
GeneralRe: Bleah! Pin
Member 32560313-May-17 21:46
professionalMember 32560313-May-17 21:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.