· engineering  · 6 min read

Revisiting: BitWise flags

Combining multiple booleans into a single integer with bitwise flags

Combining multiple booleans into a single integer with bitwise flags

Bitwise flags may seem complicated at first, but once understood, they’re a useful tool that allows us to represent multiple boolean values (true/false) as a single integer.

The ability to combine multiple booleans into a single number is particularly useful when data storage or transfer efficiency is a concern.

For example, imagine a relational database schema where a single column stores “user permissions” using bitwise flags instead of a separate column for each permission (enabled, publisher, editor, etc.). This reduces storage requirements and simplifies queries, and removes need to adjust the schema if an extra user state is introduced.

How it works

Imagine a system managing user permissions. We may wish to allocate multiple boolean values such as ‘Enabled’, ‘Published’, ‘Editor’ etc.
Even with a small number of states, maintaining, and storing all these booleans becomes cumbersome.

Begin new

How it works

Imagine a system managing user permissions. We may wish to allocate multiple boolean values such as “Enabled,” “Published,” “Editor,” etc. Even with a small number of states, maintaining and storing all these booleans becomes cumbersome.

By assigning each state a unique power of 2, we can combine all our states into a single number:

Enabled  = 1
Published = 2
Editor = 4
Admin = 8

Enabled + Admin = 9
Enabled + Published + Editor = 7

Because the values increase by powers of 2, there are no overlapping ways of combining them, so each number represents a unique set of states.

This is where bitmasking comes into play. Bitmasking is a technique used to set, clear, or toggle specific bits in a number. Using bitwise operations like AND (&) and OR (|), we can manipulate these bits and determine which permissions are set in a user’s flag value.

For example, consider the same user permissions with their corresponding binary values:

Enabled   =  0001
Published  = 0010
Editor  =    0100
Admin  =     1000

Enabled + Admin = 1001
Enabled + Published + Editor = 0111

Here, the term “bitwise flags” refers to representing multiple boolean states using bits, while “bitmasking” refers to the operations that manipulate these bits. Bitmasking helps set, clear, or check specific bits within a number.

By using bitmasking, we can perform operations such as setting and unsetting bits. For instance:

function setFlag(flags: number, flag: UserFlags): number {
  return flags | flag; // Use bitwise OR to set the desired bit
}
function unsetFlag(flags: number, flag: UserFlags): number {
  return flags & ~flag; // Use bitwise AND with NOT (~) to clear the bit
}
let userFlags = 0;
userFlags = setFlag(userFlags, UserFlags.Enabled); // Set enabled flag
userFlags = unsetFlag(userFlags, UserFlags.Admin); // Unset admin flag

Checking whether a bit is set can also be done with the & operator:

function isFlagSet(flags: number, flag: UserFlags): boolean {
  return (flags & flag) === flag; // Use bitwise AND to check if the bit matches
}
 
const isAdmin = isFlagSet(userFlags, UserFlags.Admin); // Check admin status

By understanding both bitwise flags and bitmasking, you can efficiently represent and manipulate multiple boolean values within a single integer.

end new

By assigning each state a unique power of 2, we are able to combine all our states as a number into a single variable:

Enabled  = 1
Published = 2
Editor = 4
Admin = 8

Enabled + Admin = 9
Enabled + Published + Editor = 7

Because the values increase by powers of 2, there are no overlapping ways of combining them, and thus each number represents a single set of states.

Using bitwise operations like AND (&) and OR (|) we can manipulate these bits and determine which permissions are set in a user’s flag value.

Of course, what we are actually doing is setting and unsetting bits. Consider the same example above, with the numbers represented in binary.

Enabled   =  0001
Published  = 0010
Editor  =    0100
Admin  =     1000

Enabled + Admin = 1001
Enabled + Published + Editor = 0111

At this point it should be apparent that as we are setting bits in a number, we are limited in options to the number of bits our numeric data type can hold. For example a 32-bit integer will be able to hold 32 unique flags. If you find yourself approaching this limit though, you may be doing something wrong.

Bitwise in practice

Implementing bitwise is very easy, the examples below are in TypeScript, but a solution in another language, for example PHP, would be almost identical save for some syntactical differences.

Using the shift left operator, we can shift the number on the left to the number on the right, so we can define our bits like this:

enum UserFlags {
  Enabled = 1 << 0, // = 1
  Published = 1 << 1, // = 2
  Editor = 1 << 2, // = 4
  Admin = 1 << 3, // = 8
}

Then we can use our bitwise operators to set/unset the bit.

function setFlag(flags: number, flag: UserFlags): number {
  return flags | flag; // Use bitwise OR to set the desired bit
}
function unsetFlag(flags: number, flag: UserFlags): number {
  return flags & ~flag; // Use bitwise AND with NOT (~) to clear the bit
}
let userFlags = 0;
userFlags = setFlag(userFlags, UserFlags.Enabled); // Set enabled flag
userFlags = unsetFlag(userFlags, UserFlags.Admin); // Unset admin flag

Checking whether a bit is set is also be done with the & operator:

function isFlagSet(flags: number, flag: UserFlags): boolean {
  return (flags & flag) === flag; // Use bitwise AND to check if the bit matches
}
 
const isAdmin = isFlagSet(userFlags, UserFlags.Admin); // Check admin status

And that’s all, by representing our variables as bits, we can combine multiple booleans into a single number, and query it to see whether our booleans are set or not.

Conclusion

Bitwise is a fantastic way to represent several boolean values in a compact single number.
Due to it’s efficiency its used often, for example you’ll find it when setting file permissions, or in network packets.

Though bitwise flags are a long established pattern, anecdotally at least, their popularity has decreased somewhat in the past years, perhaps due to a reduced emphasis on resource cost (storage/bandwidth got cheaper), and a rise in schemaless data stores (noSql).

Though awareness and recognition of bitwise ought to be expected, be sure its use is documented in code to avoid misunderstandings, and remember the limit on the amount of flags an integer can hold.

James Babington

About James Babington

A cloud architect and engineer with a wealth of experience across AWS, web development, and security, James enjoys writing about the technical challenges and solutions he's encountered, but most of all he loves it when a plan comes together and it all just works.

Comments

No comments yet. Be the first to comment!

Leave a Comment

Check this box if you don't want your comment to be displayed publicly.

Back to Blog

Related Posts

View All Posts »