DEV Community 👩‍💻👨‍💻

Ian Parr
Ian Parr

Posted on

Taking advantage of BitMasks

Binary
Recently I needed a way to store a finite list or error messages that could potentially occur during an async process.

I could have implemented something like - creating a relational table structure, store them as a JSON file etc. All of the alternatives felt a bit like 'overkill' for reasons I won't go into here. So I decided to opt for a Bitmask. Why? Well, essentially, bitmasks are a great way to store a large amount of 'information' in a small space.

Here are the potential errors that I needed to log.

  • invalidName
  • invalidDate
  • invalidTime
  • invalidType
  • invalidRange
  • invalidState

So what Bitmasking allowed me to do was to assign an integer value to each of the potential errors. So:

  • invalidName = 1
  • invalidDate = 2
  • invalidTime = 4
  • invalidType = 8
  • invalidRange = 16
  • invalidState = 32

Usage
In C# land we would implement something like this:

using System;

public class Program
{
    static int _errorTotal = 0;

    public static void Main()
    {           
        //simulate some errors
        _errorTotal += (int)Errors.Name;
        _errorTotal += (int)Errors.Date;
        _errorTotal += (int)Errors.Time;
        _errorTotal += (int)Errors.Type;

        Console.WriteLine("Error total: {0}", _errorTotal); // this is what we persist
        Console.WriteLine("Error list: {0}", (Errors)_errorTotal); // this is how we read the data back out
    }

    // Holds the value of each of the error types
    [Flags]
        public enum Errors 
    {
        Name = 1,
        Date = 2,
        Time = 4,
        Type = 8,
        Range = 16,
        State = 32
    }
}
Enter fullscreen mode Exit fullscreen mode

Output:
Error total: 15
Error list: Name, Date, Time, Type

We can now see how storing just one integer value, you can gain access to any combination of errors that occurred during the async process.

Note: Like I said at the start, this is a great way of storing a finite list of 'stuff' - for particularly long lists of 'stuff', you might want to cast your [Flag] enum to (long):

[Flags]
public enum Errors : long
{
    Name = (long)1,
    Date = (long)2,
    Time = (long)4,
        ...
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
kspeakman profile image
Kasey Speakman • Edited on

Alternative syntax to set flags.

_errors = _errors | Errors.Name;
_errors = _errors | Errors.Date;
...

// check present of a flag
var hasDateError = _errors.HasFlag(Errors.Date);

Alternative syntax to define them using C# 7 binary literals

[Flags]
public enum Errors : int
{
    // to represent absence of any flag
    None = 0b0000_0000_0000_0000,
    Name = 0b0000_0000_0000_0001,
    Date = 0b0000_0000_0000_0010,
    Time = 0b0000_0000_0000_0100,
    ...
}

Or shift syntax

[Flags]
public enum Errors : int
{
    // to represent absence of any flag
    None = 0,
    Name = 1 << 0, // 1
    Date = 1 << 1, // 2
    Time = 1 << 2, // 4
    ...
}
Collapse
 
rubberduck profile image
Christopher McClellan • Edited on

Something that makes things a little more clear and easy to work with is if you define the enum in terms of powers of 2.

[Flags]
public enum errors
{
    Name = 2^0,
    Date = 2^1,
    Time = 2^2,
    // etc.
}

Sadly, C# lacks a power operator.

Collapse
 
engineercoding profile image
Wesley Ameling

I really like this approach, as I usually use this too for even other applications. But instead of adding, I usually use a bitwise OR to add them together. While it really does not matter in this particular case, it feels more like using bitwise operations. Any particular reason for using an add operator instead of a bitwise OR?

We are hiring! Do you want to be our Senior Platform Engineer? Are you capable of chipping in across sysadmin, ops, and site reliability work, while supporting the open source stack that runs DEV and other communities?

This role might just be for you!

Apply now