How to use bitwise operations to compactly store small values in a single number
Tags: binary, bitwise, boolean, javascript
Published: 2021-05-14 12:08:23

---
title: How to use bitwise operations to compactly store small values in a single number
published: true
description: A quick deep dive into binary world to learn how to **compactly store small values** needed by our web applications in a single 32-bit unsigned integer.
tags: binary, bitwise, boolean, javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vq0i77idkbwuejf8yk7y.jpg
---

Computer science is full of magic, which is often obscured from our eyes these high-level days. And rightly so: usually to implement features needed by our users we don't need any fancy CS knowledge.

Yet sometimes you may get something from knowing basics of computer science, such as bitwise operations and binary code in general.

I won't go into much detail about how decimal, binary and other numeral systems work. Instead, I'll show you how to **compactly store small values** needed by our web applications in a single 32-bit unsigned integer.

> 32 bits as a cap aren't selected randomly. This is the limit for a JS interpeter to perform binary operations.

## Why store something in a compact form?
Let's start with a typical single page web application. For instance, a spreadsheet editing app with multiple panels and windows sprinkled all over the screen.

![Spreadsheet App with Panels](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ay0pmronmws0vdjiy548.png)

We will also assume our users may benefit from sharing links to the documents they create _and_ restoring exact UI configuration so that it's easier to resume work from the state we left it off at.

So our app has 10 panels, and we need to encode state of these panels (open or closed for simplicity) in URLs they share.

You typically would create a URL similar to this: `?panel1=on&panel2=off&panel3=on` and so on. It's easy to see how wordy this becomes even with 10 panels, and what if other parts of our URLs are important too? So we don't want to end up sharing something like this:

![Ridiculous URL](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4bkv0i8yesks59cwp2q.png)

What if instead, we could use a single URL parameter, say `?panels=626` to encode all these values at once, in a single number?

This is possible, thanks to the bitwise operations magic, and I'll show you how exactly.

## Basics of bitwise operations
If you paste `parseInt('1001110010', 2)` into a JavaScript interpeter prompt and hit enter, you'll see the decimal number - `626`. This is how these _10 bits_ are encoded into a numeric value in decimal numeral system.

By definition, a bit is a value represented by two possible states - 1 and 0. Exactly what we need to encode a `true` or `false` in the most compact form. So we can use these mechanics to store panel states (visible or hidden)!

Let's try doing so by hand.

We will count bits from right to left, first bit having the index of `0` and last having `9`. These are, not coincidentally, powers to which you have to raise the binary base `2^n` or `Math.pow(2, n)` to get numbers represented by these bits.

### Using exponentiation and binary shifts to store and restore values
So to store the state of panels, we may use the following code:

```javascript
const panelStates = [
  false,
  true,
  false,
  false,
  true,
  true,
  true,
  false,
  false,
  true,
];

let result = 0;

for (let i = 0; i < panelStates.length; i++) {
  const bit = panelStates[i] ? Math.pow(2, i) : 0;
  result = result | bit;
}

console.log(result);
```

You may paste the code above into any JS interpreter and see that this code, indeed, prints the expected number `626`.

But why? In the loop, we applied the binary `OR` operation represented in JavaScript by the pipe sign `|` to the `result`. As the second operand, we used 2 raised to the power of `index`, which is, not coincidentally, the number of bit when counting from right to left, starting from zero.

Magic? No, it's binary encoding in all its beauty.

But hey, you should say now, we don't need to only to encode, we need to get our values back too! Let's do so.

```javascript
const panelStatesEncoded = 626;
const panelStates = [];

for (let i = 0; i < 10; i++) {
  const mask = panelStatesEncoded & Math.pow(2, i);
  const bitValue = mask >> i;
  panelStates.push(Boolean(bitValue));
}

console.log(panelStates);
```

So the last line of this code will expectedly print an array with the same values we started from.

Why? Well, this code includes a few more binary operations we have to understand. But there is nothing impossible for a computer science magician, isn't it?

First, we start with looping from 0 to 9, inclusive, as we know exactly how many boolean values we're looking for in a number.

The operation we need to perform next is _binary masking_ using a logical `AND` operator represented by `&` in JavaScript. So we know that a particular bit in our `panelStatesEncoded` number represents state of an N-th panel. Therefore, we need to somehow choose it and only it.

This is done by the `AND` operator: when we do `Math.pow(2, 3)` for the 3rd panel state, for example, we get 8, which is `1000` in binary code. `1000 & 1011`, where the first number is a mask and the second one is our encoded panels state's first 4 bits, we get `1000`.

This is because logical `AND` only leaves the bits that are present in both values on. Had we used `0011` as our second operand, AND would yield `0000`, which is simply `0`.

But then `1000` we get from the operation is 8, not `true` or `false`, or anything else meaningful. So we have to _shift it to the right_ using binary shift operator `>>` 3 times (our index, the power of 2 that is 8) to get a single bit.

A single bit, a 0 or 1, is easily converted into a boolean value using the `Boolean` conversion, and we can push it to the array of values.

Our puzzle is now complete. We can toggle right bits by doing `Math.pow(2, n)` or actually simply doing binary shift to the left `1 << n`, which is the exact equivalent of raising 2 to the power of `n`. Then we can decode, applying a _bitmask_ and shifting it back to the right `n` times.

## Abstracting complexity away
Hopefully at this point you're as much thrilled as I am. Even decades after getting into computers, I am still excited to make them do what I want speaking the same language they do. The almighty binary.

But isn't it too tedious to write by hand and, perhaps, even too error-prone and complicated to be used in production-ready applications?

Indeed, it is! So I created a library to abstract unnecessary complexity away (yet I'd still argue you have to know how it works under the hood). Make some noise for [`bitwise-options`](https://github.com/oleggromov/bitwise-options). Yay!

Not only does it allow you name your options and then read them from and write to a single 32-bit integer, it also makes it possible to store multiple _unsigned_ integer values in a single number.

For example:

```javascript
import BitwiseOptions from 'bitwise-options';

// Configure available options
const options = new BitwiseOptions([
  {name: 'boolean'}, // single-bit boolean by default
  {name: 'uint_single', type: 'uint'}, // single-bit unsigned int
  {name: 'uint_3bit', type: 'uint', size: 3}, // 3-bit unsigned integer in range of [0, 7]
]);

options.read(26); // 11010 in binary

console.log(
  options.get('boolean'), // false
  options.get('uint_single'), // 1
  options.get('uint_3bit'), // 6
);

options.set('uint_3bit', 0);
console.log(
  options.get('uint_3bit'), // 0
);

console.log(
  options.toNumber(), // 2
);
```

You can find the library on [GitHub](https://github.com/oleggromov/bitwise-options) and [npm](https://www.npmjs.com/package/bitwise-options).

Yes, I was too lazy to implement signed integer support but will be happy to do so if you:
- enjoyed reading the article as much as I enjoyed writing it
- give the library a star on github so that more people get to know about it
- **[follow me on Twitter](https://twitter.com/oleggromov)**, where I write about things worth knowing as a software person if you seek independence and satisfaction 

Thank you for you attention and let me know in the comments if you found this useful and why?