Bit Masking: A Practical Example

How to store data on teeny-tiny bits

All your base are belong to us

If you're a programmer, you may already have some notions of numeral systems and you've interacted with a couple of them.

If not, then it'll be a bit harder to feel at home going forward and it may help to get familiar:

For understanding the following example, it is very important to understand the binary system and its operators. In it, we’ll use only the & (AND) and ^ (XOR) operators. AND will be used to determine whether a particular bit is 1 or 0. XOR will be used to switch particular bits between 1 and 0.

There are 10 types of people in this world, those who understand binary and those who don’t.

The practical example

Our computers work with the binary system and that means that the digits that can be used at any given position are either 0 or 1.

The values of 0 and 1 can be considered to be false and true, and here things become interesting for the example we'll explore.

We’ll use bits to store a user’s email notifications settings. More exactly the binary representation of an integer.

For that, I created the following CodePen.

The notifications settings

This is a clone of the CodePen's Email Notifications page. I’ll not explain the Flutter and Dart code that went into creating the UI. Instead, I’ll focus on what allows us to store a user’s email notifications preferences in binary within a single integer.

It all starts with creating a Flutter StatefulWidget. This type of widget provides methods to intialize and update its state.

class Notifications extends StatefulWidget {
@override
_NotificationsState createState() => _NotificationsState();
}
class _NotificationsState extends State<Notifications> {
int notificationsSettings = 0;
}

To store the settings we'll need as many bits as we have items in the list and, in our current case, that is 6. So the binary representation of the items when everything is selected will be 111111, and the decimal one is 63.

If we want to have all items opted-in by default we'll have to set the notificationsSettings value to 63.

In Dart, we can use int’s toRadixString and tryParse providing the 2 radix to toggle between the binary String representation of a number and its decimal integer representation.

@override
void initState() {
notificationsSettings = 63;
String binary = notificationsSettings.toRadixString(2);
print('Binary $binary');
int decimal = int.tryParse(binary, radix:2);
print('Decimal $decimal');
super.initState();
}

The above code will print:

Binary 111111
Decimal 63

In a default Flutter Column, items are arranged from top to bottom and that means that the top-most item will correspond to position 0, next one to position 1, and so on until the last item that corresponds to n-1, where n is the number of items our list.

In our case we won’t be working with an item index but with a provided value that represents the position of the item (top to bottom as we see the items in the list). This value is a bitmask that will help us check and toggle the setting between true or false.

So for our 6 items, we'll have:

item 0 with value 1  (mask 000001)
item 1 with value 2 (mask 000010)
item 2 with value 4 (mask 000100)
item 3 with value 8 (mask 001000)
item 4 with value 16 (mask 010000)
item 5 with value 32 (mask 100000)

When we create our UI we use inside the items a Flutter Switch widget which takes as value true or false.

value: notificationsSettings & item.value == item.value

Let's say we look at the last item to introspect. For humans that will translate into:

value: 63 & 32 == 32

Our computers work in binary so for them the integers will look like this:

value: 111111 & 100000 == 100000

The above AND binary operation will result in 100000 == 100000 which in the end evaluates to true and that makes our item to be toggled On.

Image for post
The last item in the notifications settings list toggled to On

Providing 63 as the initial value for the notificationsSettings variable we have all of our 6 items toggled On by default.

When we press on an item's Switch we trigger its onChanged method.

onChanged: (bool val){
setState(() {
notificationsSettings = notificationsSettings ^ item.value;
});
}

Here we update the StatefulWidget's state and toggle the bit corresponding to our item between true and false.

notificationsSettings = notificationsSettings ^ item.value;

Let’s take again the last item in focus. For humans that will translate into:

63 = 63 ^ 32;

Computers still work in binary, so for them, the above will look like this:

111111 = 111111 ^ 100000

The above XOR binary operation will result in 111111 = 011111 which will make our last item to be toggled Off because the bit at its position is now 0 and the below expression evaluates now to false.

value: 011111 & 100000 == 100000
Image for post
The last item in the notifications settings list toggled to Off

So here we have it. A way to read and update multiple notifications settings all stored inside a single integer number.

We used bits to store a user’s email notifications settings.

I find this very easy to use and use it quite often within my projects. I have more practical use cases.

If you want to find more such use cases, please let me know and I'll be happy to bring more to the surface. Some are below:

Conclusions

There is some debate about whether writing code this way and using this is a good practice or not.

I find it less verbose and easier to implement (probably because I'm familiar with it).

Persisting settings this way in a database require only one field. It can be either a binary String representation with the predefined length for how many bits we need or a decimal representation with the length of that of the maximum number we'll have. In our case, for 6 items and using MySQL, would be VARCHAR(6) for a String representation ('111111') or INT(2) for the decimal representation (63).

Certainly some size is saved when transferring these settings. Compare the 2 following JSONs:

// CodePen's verbose way...
var verbose = {
"userNotification":
{
"emailMarketingSpark": false,
"emailMarketingChallenges": true,
"emailMarketingFeatures": true,
"emailSocialComments": true,
"emailSocialFollows": true,
"emailSocialHighlights": true
}
};
// ... and the equivalent, less verbose way.
var lessVerbose = {
"userNotification": 62
};

Considering you work in binary directly with bits of data you also get some performance improvements when we talk of large scale applications and/or machine to machine communication. On small scale applications, these won't be noticeable.

Also, having all settings stored, either in the browser's cookies or in a database, in a single integer can be good for obfuscation. How would that help? Let's say the data gets compromised. Just looking at the data stored this way won't tell much to the evil minds that got their hand on your precious data (or worse on your users' data).

Certainly, on large teams and projects, these benefits can quickly turn into problems. Some developers might not understand binary as well as others, and onboarding might become cumbersome if the code is not documented well enough.

Another trap of this approach is the fact that the length of items is well known: 32 if you work with a 32bit integer or 64 if you work with a 64bit integer. Some workarounds are possible, one of which is to store data like strings and separate the representations with another character like _. This would make using that data a little difficult and direct SQL querying would be very difficult.

Last to mention on the list of traps is that the stored information is quite tight to the positions in the binary representation. Modifying all stored settings is possible with some bitwise shifting, but I'd recommend using this technique if you need it in a predefined fixed scenario.

It is useful to add the skills needed to work with bits to your skillset. In the end, there are 10 types of people in this world, those who understand binary and those who don’t.

Tha(nk|t’)s all!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store