Table of Contents
If you are making a web application with users management, bitwise flags will be really useful for that same application. I've never used bitwise flags in my past small web application, and I definitely regret not having used them before... Not only it's super convenient, but you also don't need tons of rows in your database to store various account information. I plan on using bitwise flags in a future project as well, hence this blog post explaining how it works!
PS: This will most likely be my last blog post focused on software engineering, I will focus more on cyber security in the future, as this was the main goal of my blog when I created it...
Operators Refresh
Before jumping into what bitwise flags are and how to use them, it's needed to get to know the three operators that will be used.
OR
Operator
The OR
- I will also use |
- is a basic operator that you most likely have already used while coding. Here's a small refresh on the result of an OR
operation:
A | B | A | B |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
AND
Operator
Similar to the OR
operator, the AND
- I will also use &
- operator is a basic operator you most likely have already used. Here's the table as well:
A | B | A & B |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Left Shift Operator
The left shift - I will also use <<
- operator is an operator you may have not used yet. For bitwise flags it is one of the base that is needed to make it working. Let's take a look at how it works, it's really easy.
Let's take as example 1 << 4
:
- We first write down
1
as binary, which is obviously0000 0001
- We then move that entire binary number by
4
to the left, so we are left with0001 0000
Easy, right...? Here's a different way of showing it off:
0000 0001 << 4:
1. 0000 0010
2. 0000 0100
3. 0000 1000
4. 0001 0000
So 1 << 4
is equals to 16. Yes, we can definitely write that down, though it gets more readable to use left shifts once you're dealing with big numbers such as 1 << 23
as you may not know that 8388608
actually represents this...
How do bitwise flags work?
All those operators are very wonderful, but what are actually bitwise flags...?
Well, very simple. It's a way to have many ON/OFF toggles in a single number. Let's quickly show that off with an example, you will also see how the OR
and left shift operators will be used.
package main
import "fmt"
// Set some user account flags
const (
EMAIL_VERIFIED = 1 << 0
ACCOUNT_BANNED = 1 << 1
)
// Create the Account structure
type Account struct {
Username string
Email string
Flags int // Here there will be all account flags, e.g. whether the account is currently banned
}
// We create the function to check if the account is banned
func (a *Account) IsBanned() bool {
return (a.Flags & ACCOUNT_BANNED) == ACCOUNT_BANNED
}
Now the, maybe, big question: "How does this even work???"
Check if flag exists
Well, checking if a flag is existent is very simple. Considering we always use 1 << X
, when performing the left shift we will always and only move a binary 1 around. Then checking for a flag is as simple as using the AND
operator. Let's suppose the user's account Flags
field is 18
.
18 => 0001 0010
ACCOUNT_BANNED => 1 << 1 => 0000 0010
0001 0010
0000 0010 AND
-------------
0000 0010 -> This is equals to ACCOUNT_BANNED
Hopefully with that example above you now understand why (a.Flags & ACCOUNT_BANNED) == ACCOUNT_BANNED
can be used to check if a flag is existing.
a.Flags
value
Creating the To create the value that will be needed to be stored in a.Flags
is also very simple. Let's suppose our account is both verified and banned, our flag will need to contain both of these values.
EMAIL_VERIFIED => 1 << 0 => 0000 0001
ACCOUNT_BANNED => 1 << 1 => 0000 0010
0000 0001
0000 0010 OR
------------
0000 0011
Now you most likely understand why the pattern is using 1 and not some other number in 1 << X
, other numbers may overwrite other flags. Now we convert 0000 0011
to decimal and we get 3
- that is the value for a.Flags
. We can always add new flags with newValue = oldValue | flag
.
Why and where to use bitwise flags?
Now I understand that the explanation above is not the best, I may edit the post based on comments to clarify some things if needed - but maybe with some examples when bitwise flags may be useful, you may understand it better.
Roles
Without bitwise flags
Let's suppose you need roles in your web application, totally understandable. How would you do it?
Maybe an ENUM
field in your database if an account can only have one role. But what if it can have more than one? Maybe create a table roles
that looks like the following:
user_id | administrator | moderator | member |
---|---|---|---|
1337 | 1 | 1 | 1 |
7331 | 0 | 1 | 1 |
7337 | 0 | 0 | 1 |
You get where this is going... Horrible table with just a row per role, yikes.
With bitwise flags
With bitwise flags, you just need a single row roles
in your user's table :) Then you assign for every role a left shift operation:
const (
ADMINISTRATOR = 1 << 0
MODERATOR = 1 << 1
MEMBER = 1 << 2
)
After that use the techniques above to create the value for roles
, for the user 7331 for example: roles = MODERATOR | MEMBER
. Then we can check if a user is an administrator with something like that:
func (u *User) IsAdministrator() bool {
return (u.Roles & ADMINISTRATOR) == ADMINISTRATOR
}
Same goes for other account flags as shown above, to know if an account has the email verified or if they are banned.
Permissions
Same as roles, if you need different users to have permissions on different functionalities of your web application, it may be totally worth it using bitwise flags.
Remove unnecessary functions
Now this one may not be the biggest strength of bitwise flags, but it can definitely be used to optimize some functions in your code. Yes, functions.
Without bitwise flags
Let's imagine we want to print some information about a human:
package main
import "fmt"
type Human struct {
Firstname string
Lastname string
Username string
Email string
Age int
}
func (h *Human) PrintData() {
fmt.Println("Firstname:", h.Firstname, "Lastname:", h.Lastname, "Username:", h.Username, "Email:", h.Email, "Age:", h.Age)
}
func main() {
myHuman := &Human{
Firstname: "John",
Lastname: "Doe",
Username: "jdoe",
Email: "[email protected]",
Age: 1337, // Yes that human it quite old...
}
myHuman.PrintData()
}
Now this works, as we get the expected output. But what if we want to print a single field? What if you don't want to print all of the fields? Well, yes you can do the following for all of your fields
func (h *Human) PrintFirstname() {
fmt.Println("Firstname:", h.Firstname)
}
But here again, what if you want only 3 of them? Well, you can replace Println
to Print
and call the 3 functions to print those fields. But cool kids use bitwise flags
With bitwise flags
Here again, assign each field a left shift operation and we copy the structure of above:
package main
import "fmt"
type Human struct {
Firstname string
Lastname string
Username string
Email string
Age int
}
const (
FIRSTNAME = 1 << 0
LASTNAME = 1 << 1
USERNAME = 1 << 2
EMAIL = 1 << 3
AGE = 1 << 4
)
And now we can create the function needed to print each field, depending on the parameter - the flags - we give to the function.
func (h *Human) PrintDataWithFlag(flags int) {
// The " " at the end is just to make sure it looks good when printing multiple things
if (flags & FIRSTNAME) == FIRSTNAME {
fmt.Print("Firstname: ", h.Firstname, " ")
}
if (flags & LASTNAME) == LASTNAME {
fmt.Print("Lastname: ", h.Lastname, " ")
}
if (flags & USERNAME) == USERNAME {
fmt.Print("Username: ", h.Username, " ")
}
if (flags & EMAIL) == EMAIL {
fmt.Print("Email: ", h.Email, " ")
}
if (flags & AGE) == AGE {
fmt.Print("Age: ", h.Age, " ")
}
}
Now what if we want to just print the Firstname
, the Email
and the Age
fields? Very simple:
func main() {
myHuman := &Human{
Firstname: "John",
Lastname: "Doe",
Username: "jdoe",
Email: "[email protected]",
Age: 1337, // Yes that human it quite old...
}
myHuman.PrintDataWithFlag(FIRSTNAME | EMAIL | AGE)
}
Now we have a clean way of printing all the 3 fields, without calling a different function 3 times. You can see the full code here.
Conclusion
If you've mangaged to read and understand all the way down until here and haven't given up on my poor explanation, congrats! Bitwise flags are somewhat complicated for newcomers but also easy once you understood the basics of it and where/how to use them. I personally plan on using bitwise fields for roles and account flags in Project Absence. I don't really like having multiple rows in my datbase just to store such information, I'd rather have a single fields
row.