Saturday, October 30, 2010

Bitmasks demystified

Hi guys!
Using bitmasks, has been always a powerful technique for storing small set of different flags in only one variable. A flag can represent anything, which we may want to check whether is ON or OFF.

What is a bitmask?
Bitmask only sounds scary and complex, but it actually is something very simple. It's only the name of the representation of one number in Binary Notation. For instance, let's look at a few representations:

  • 00010 is the number 2

  • 00100 is the number 4

  • 00101 is the number 5


Still, we don't see the number 5 as "00101", but we can use Bitwise Operators to check whether on some position of the binary representation of the number there is 0 or 1.

What can we use a bitmask for?
The bitmask can be used to store some states. In C#, there is the FlagsAttribute which actually states, that the enum we're creating will be used for bit manipulation. One of the most popular enums, which is decorated with the FlagsAttribute is the BindingFlags enum.
So, if we want to store small number of flags, we can use the bitmask.

How does the bitmask work?
By setting as number values of our Enum members the powers of the 2, we end up having an enum collection like the one below:
[Flags]
public enum CustomFileMode : int
{
Create = 1,
Open = 2,
Append = 4,
Truncate = 8
}

The bit representation of the same collection is as follows:

  • Create - 0001

  • Open - 0010

  • Append - 0100

  • Truncate - 1000


Now, if we have the number 5 (0101), using the Bitwise Operation &, we can check whether any of the Enum members is set on. How do we perform the check? We evaluate the Flag & Bitmask statement. This will give us a positive result, if the Flag is set as On in the Bitmask. (i.e. it's 1 in the bitmask's binary representation).


When checking for Truncate flag, we evaluate the (CustomFileMode.Truncate & 5) statement and see that the result is (1000 & 0101 = 0000), so the Truncate flag is not turned on. (because, the result is zero)


If we check for the Append flag, we evaluate the (CustomFileMode.Append & 5) statement and the result is (0100 & 0101 = 0100), which is greater than zero - so the Flag CustomFileMode.Append is turned on.


As we have selected only numbers which are a power of the 2, we don't have any duplicate values in binary representation, as a power of the 2 is always having just one 1 in it's binary representation.

How do we set a flag? We use the bitwise operator | (OR). Let's start with an empty number 0 and we want to set the flags Append and Create. What we can do is:

0000 | Append (0100) = 0100
0100 | Create (0001) = 0101

We now see, that in the result 0101, we have both Append and Create flags set.

How do we use a Bitmask in C#
We'll examine one simple example, which outlines how easy it is to create a bitmask in C#. The steps to follow are:
1. Create a Enum which derives from an integer class
2. Decorate the Enum with FlagsAttribute
3. Create the values of the Enum, using only powers of the 2
4. Write your own business logic for checking the Flags in a bitmask using the Enum
Here is a sample Console Application code, which you can play with to explore the Bitmasks:
using System;
using System.Collections.Generic;

namespace BitmaskExample
{
public class Program
{
static void Main(string[] args)
{
// Create a new CustomFileMode. We set the Open, Create and Truncate operations on.
CustomFileMode fileModes = CustomFileMode.Open | CustomFileMode.Create | CustomFileMode.Truncate;

if (IsFlagSet(fileModes, CustomFileMode.Append))
{
Console.WriteLine("Append flag is set.");
}

if (IsFlagSet(fileModes, CustomFileMode.Open))
{
Console.WriteLine("Open flag is set.");
}

if (IsFlagSet(fileModes, CustomFileMode.Create))
{
Console.WriteLine("Create flag is set.");
}

if (IsFlagSet(fileModes, CustomFileMode.Truncate))
{
Console.WriteLine("Truncate flag is set.");
}
}

/// <summary>
/// Checks if the flag is turned on in the bitmask
/// </summary>
/// <param name="bitmask">The bitmask - consisting of all the Flags turned on</param>
/// <param name="flag">The flag we're checking for</param>
/// <returns>True if the flags is on, otherwise returns false.</returns>
static bool IsFlagSet(CustomFileMode bitmask, CustomFileMode flag)
{
return (bitmask & flag) != 0;
}
}

/// <summary>
/// The CustomFileMode is an Enum, which states are with values the power of 2.
/// </summary>
[Flags]
public enum CustomFileMode : int
{
Create = 1,
Open = 2,
Append = 4,
Truncate = 8
}
}

In Conclusion
I hope, this post sheds more light for you on the Bitmask concept. Your questions and suggestions are as always welcome!

Tip
You can choose a .NET type to inherit your Enum colleciton, based on how many different flags you need to set and check. The range of values, that can be stored depends on the number of bits in the type. Below are the 3 most common types:
short (8 bits) - up to 7 different flags.
int (32 bits) - up to 31 different flags.
long (64 bits) - up to 63 different flags.