Get Wise on Bitwise

programming, computerscience, javascript

5 minute read

03/02/2022

What are bitwise operators?

JavaScript has a lot of operators built in, these operators allow us to preform basic operations with values. Some operators you might be familiar with are arithmetic operators(<code> +, -, *, /, %</code> ), logical operators(<code> &&, ||, ! </code>), and comparison operators(<code> <, >, = </code>). Bitwise operators aren't used very often in Javascript but they are always there if you need them. Bitwise operators allow us to manipulate numbers in binary. JavaScript takes the two binary values and compares them, returning a new value depending on the operator and it's operands. You should have some understanding of binary and how binary and decimal values relate to one another before using bitwise operators. More information on binary numbers can be found at this link. <br >

AND &

The bitwise AND operator is represented by a single ampersand instead of two for the typical AND logical operator. Bitwise AND will set each bit to 1 if both bits are 1. If you were to evaluate something like <code> 5 & 1 </code> which is the same as <code> 0101 & 0001 </code> in binary, the return value would be 0001. Only bits that are both 1 will remain as a 1, otherwise they will become a 0. <br >


5 & 1 // 0101 & 0001    
                       
=> 1 // 0001

OR |

The bitwise OR operator is represented by a single vertical bar instead of the two you'd see with logical OR. Bitwise OR will set each bit to 1 if one of the two bits is 1. If you were to evaluate 5 and 2 with OR it would look something like this <code> 5 | 2 </code> and would return <code> 0111 </code>. Bitwise OR will keep any 1 that shows up in either binary number. <br >


5 | 2 // 0101 | 0011

=> 7 // 0111

XOR ^

Bitwise XOR is represented by the carrot or chevron symbol. XOR or 'exclusive or' will set each bit to 1 if only one of the two bits is 1. Evaluating <code> 5 ^ 1 </code> would return <code> 0100 </code>. If both bits are set to 1 it will change them to zero's and if only one bit is 1 it will change to a 1. <br >


5 ^ 2 // 0101 ^ 0011

=> 6 // 0110


NOT ~

Bitwise NOT is represented by a tilde. NOT is a unary operator, meaning it only takes one operand inverting all of the bits of the operand. Evaluating <code> ~ 5 </code> would return <code> 1010 </code>. NOT will make all 0's 1's and all 1's 0's. The value that results from using NOT in an operation is called a complement, having the exact inverse of the bits of the initial value. <br >

~5 // 0101

=> -6 // 1010

It's important to note that when using bitwise operators JavaScript is using 32-bit numbers for the comparison, not 4. So <code> ~5 </code> is actually flipping the first 28 bits 0's into 1's and flipping the last four bits to <code> 1010 </code> <br >

Use Cases

There aren't very many use cases for bitwise operation, but that doesn't mean there aren't any. One way you could use some of the bitwise operators is setting and checking user permissions. Lets say you have four different permissions you could give to a user- read, write, execute, and delete. Using just 4 bits you can give a user any combination of permissions that they need to have.

//Read, Write, Execute, and Delete 

0001 // Read = 1
0010 // Write = 2
0100 // Execute = 4
1000 // Delete = 8

If a user had a permissions value set to 1 they would have read permissions only, if they had a value of 2 they would have write permissions only etc. If you wanted to give a user multiple permissions all you would have to do is flip the bit corresponding to the permission you'd like to give. Meaning a user with all four permissions would have a value of 15 or <code> 1111 </code>


let userObj = {
    name: Michael,
    permissions: 3
}

The user Michael in the example above has a permissions value of 3, 3 in binary is <code> 0011 </code>, meaning Michael has permission to read and to write but is not able to execute or delete. In order to check if a user has a permission, as well as adding and removing permissions, we'll use bitwise operators. <br >

Check Permissions

We'll start by checking which permissions a user has. To do this we'll use bitwise AND to compare the value assigned to the users permissions and the value we know has a binary value equal to each permission.

const READ = 1
const WRITE = 2
const EXECUTE = 4
const DELETE = 8

let userObj = {
    name: 'Michael',
    permissions: 3
}

function checkPermissions(userObj) {
    return {
        READ: !!(userObj.permissions & READ),
        WRITE: !!(userObj.permissions & WRITE),
        EXECUTE: !!(userObj.permissions & EXECUTE),
        DELETE: !!(userObj.permissions & DELETE)

    }
}

// returns: { READ: true, WRITE: true, EXECUTE: false, DELETE: false } when userObj is passed into the function

The code above is returning a boolean for each permission we could give to a user as an object. You could use that object to check if the user has the permission needed to perform a task. Next we'll give a user a new permission, to do this we'll use bitwise OR.

Adding Permissions

const READ = 1
const WRITE = 2
const EXECUTE = 4
const DELETE = 8

let userObj = {
    name: 'Michael',
    permissions: 3
}

function addNewPermission(userObj, permission) {
   return userObj.permissions = userObj.permissions | permission
}
// Returns: 11 or 1011 in binary when userObj and DELETE are passed into the function

Again in the code above we have a user object that has a permissions value of 3, meaning that the user already has read and write permissions. Using bitwise OR we can add delete permissions because OR only flips bits that are a 1 in either of the values. Lastly we'll look at how to take permissions away from a user using bitwise XOR.

Removing Permissions

const READ = 1
const WRITE = 2
const EXECUTE = 4
const DELETE = 8

let userObj = {
    name: 'Michael',
    permissions: 3,
    checkPermissions: function checkPermissions(userObj) {
    return {
        [READ]: !!(userObj.permissions & READ),
        [WRITE]: !!(userObj.permissions & WRITE),
        [EXECUTE]: !!(userObj.permissions & EXECUTE),
        [DELETE]: !!(userObj.permissions & DELETE)
    }
}
}
function removePermission(userObj, permission){
  if (userObj.checkPermissions(userObj)[permission]){
    return userObj.permissions = userObj.permissions ^ permission
  }
}
// Returns: 2 or 0010 when userObj and READ are passed into the function

When using XOR to remove a permission we actually want to use AND first to check to make sure the user has the permission, if it wasn't a permission that the user already had they would actually gain the permission. For example if we tried to take the delete permission away from our user Michael we would end up giving Michael delete permissions. This is because Michael has a permissions value of 3, or <code> 0011 </code>. If we were to use XOR on 3 and 8(<code>1000</code>), we would get 11 or <code> 1011 </code> back as our return value, not 3, which is what we ultimately intended even though the user already only had a permissions value of 3. <br > <br >

While there are use cases for bitwise operators in Javascript there aren't many. That being said, understanding what they do and how you can use them will leave you prepared for when they do come up. For even more information on bitwise operation here are some resources I used while learning about them: <br> https://www.youtube.com/watch?v=EVvZLfJtAK8 <br> https://www.youtube.com/watch?v=mesu75PTDC8 <br > https://www.w3schools.com/js/js_bitwise.asp <br > https://www.geeksforgeeks.org/javascript-bitwise-operators/

If you have any questions please leave a comment over on dev.to

Built with Next.js, Tailwind and Vercel