Codementor Events

A practical use for bitmasks in Javascript

Published Dec 02, 2021Last updated May 30, 2022
A practical use for bitmasks in Javascript

Bitmasks are great at combining multiple true/false flags into one value.

In computer science, a mask or bitmask is data that is used for bitwise operations, particularly in a bit field. Using a mask, multiple bits in a byte can be set either on, off or inverted from on to off in a single bitwise operation.
In this article, I'm going to focus on how I use bitmasks to reduce the size of entries in a database.

Bitmasks are managed using bitwise operators which are rare and may confuse less experienced developers. ESLint even has a default setting to stop you from using them. The rationale used is bitwise operators look more like typos than actual features.

However, If used correctly, they can have a huge impact on the size, speed, and scalability of your database, which can save you a lot of money in the long run.

I'm going to show a common way I use bitmasks to store and manage a user's status.

Model

example

Each value (except 0) in Status is an incrementing power of 2. The power of 2 is significant because each value represents a bit in a binary number.

You can visualize this by laying the values of Status horizontally with the highest value at the start.

TFA_ENABLED | PAYMENT_VERIFIED | EMAIL_VERIFIED | ADMIN | ACTIVE 
-----------------------------------------------------------------
   0        |        0         |      0         |   0   |   0

As a user's Status changes, turn each corresponding bit on or off and store the integer value.

Example: An Active user with only two-factor authentication enabled
Bianry Visual Layout

TFA_ENABLED | PAYMENT_VERIFIED | EMAIL_VERIFIED | ADMIN | ACTIVE 
-----------------------------------------------------------------
   1        |        0         |      0         |   0   |   1
Users Table
id               status   
----------------|-----------
1                17 -> 10001 

We store the status as an integer in the database but treat it as a binary number in our application.

You can store up to 32 true/false flags in a bitmask. If you need to add another status value, just add it to the end of the enum. You won't have to mess around with adding another column to your database.

Next, I'll go through some examples on how to manage a bitmask value.
Create User

When a user is first created, only the ACTIVE status needs to be set.

const user: IUser = {
  status: Status.ACTIVE
  ...other user stuff
}
db.set(user)

Stored Value

Bianry Visual Layout
TFA_ENABLED | PAYMENT_VERIFIED | EMAIL_VERIFIED | ADMIN | ACTIVE 
-----------------------------------------------------------------
   0        |        0         |      0         |   0   |   1
Users Table
id               status   
----------------|-----------
1                1 -> 00001

Turn Payment Flag On

A user has provided their payment information. To flip a bit to its on state, use the |= operator.

db.update({ status: (user.status |= Status.PAYMENT_VERIFIED) })

Stored Value

Bianry Visual Layout
TFA_ENABLED | PAYMENT_VERIFIED | EMAIL_VERIFIED | ADMIN | ACTIVE 
-----------------------------------------------------------------
   0        |        1         |      0         |   0   |   1
Users Table
id               status   
----------------|-----------
1                9 -> 01001

Turn Email Verified Flag On

A user has verified their email. Use the same |= operator as before, just change the Status value.

db.update({ status: (user.status |= Status.EMAIL_VERIFIED) })

Stored Value

Bianry Visual Layout
TFA_ENABLED | PAYMENT_VERIFIED | EMAIL_VERIFIED | ADMIN | ACTIVE 
-----------------------------------------------------------------
   0        |        1         |      1         |   0   |   1
Users Table
id               status   
----------------|-----------
1                13 -> 01101

Turn Flag Off

A user's payment method has been removed. To flip a bit back to its off state, use the &= ~ operator.

db.update({ status: (user.status &= ~Status.PAYMENT_VERIFIED) })

Stored Value

Bianry Visual Layout
TFA_ENABLED | PAYMENT_VERIFIED | EMAIL_VERIFIED | ADMIN | ACTIVE 
-----------------------------------------------------------------
   0        |        0         |      1         |   0   |   1
Users Table
id               status   
----------------|-----------
1                5 -> 00101

Toggle Flag On/Off

Alternatively, you can toggle a bit on and off with the ^= operator

db.update({ status: (user.status ^= Status.PAYMENT_VERIFIED) })

Check Flag

A user has made a request for a paid-only feature. To check that they have the PAYMENT_VERIFIED flag set on their status, use the & operator.

// helper function to check any flag
const isFlagSet = (actual: number, expected: number): boolean = {
  const flag = actual & expected;
  return flag === expected;
};

const user = db.users.find(id)
const hasPayment = isFlagSet(user.status, Status.PAYMENT_VERIFIED)

I hope these examples help anyone interested in using bitmasks in their own application. They can take a bit of time to get used to, but the benefits are clear.

Discover and read more posts from Jesse Langford
get started
post commentsBe the first to share your opinion
Show more replies