Module F-5·13 min read

AND/OR/XOR/shift operations, bit flags for permissions and feature toggles, and fast arithmetic tricks.

Written by Jatin Jain Saraf · Architect of SupraScan

Module F-5 — Bit Manipulation: Flags, Permissions, and Fast Math

What this module covers: Every number in your program is already a sequence of bits — this module is about working with that sequence directly instead of through the abstraction of arithmetic. It covers the core bitwise operators, how a single integer can store dozens of independent boolean flags, and the handful of bit tricks that show up in permission systems, feature flags, and performance-sensitive code.


Binary, Briefly

Every integer in memory is a sequence of bits — 1s and 0s. The number 13 is 1101 in binary: reading right to left, that's 1 + 0 + 4 + 8 = 13 (each position worth double the one before it, starting at 1). You don't need to convert by hand often, but recognizing that an integer is just a fixed-width row of switches is the entire mental model this module builds on.

(13).toString(2); // "1101"
parseInt('1101', 2); // 13

The Bitwise Operators

Five operators work directly on the bit representation, comparing or shifting bit-by-bit rather than treating the number as a single value:

const a = 0b1100; // 12
const b = 0b1010; // 10
 
a & b;   // 0b1000 = 8  — AND: 1 only where BOTH bits are 1
a | b;   // 0b1110 = 14 — OR: 1 where EITHER bit is 1
a ^ b;   // 0b0110 = 6  — XOR: 1 where the bits DIFFER
~a;      // -13         — NOT: flips every bit (and flips the sign, due to two's complement)
a << 1;  // 0b11000 = 24 — left shift: every bit moves left, multiplying by 2 per shift
a >> 1;  // 0b0110 = 6   — right shift: every bit moves right, dividing by 2 per shift (rounding down)

Analogy: Think of AND, OR, and XOR as three different ways of comparing two rows of light switches, position by position. AND: a light turns on only if both switches at that position are on. OR: on if either is on. XOR: on only if they disagree — one on, one off.

XOR is the one worth sitting with, because its "only when they disagree" property is what makes it useful for toggling: XOR-ing a bit with 1 flips it, and XOR-ing a bit with 0 leaves it unchanged — which is exactly the mechanic bit flags rely on next.


Bit Flags: One Integer, Many Booleans

Instead of storing five separate boolean fields, you can pack up to 32 independent true/false facts into a single integer, one per bit position — commonly called a bitmask.

const CAN_READ   = 1 << 0; // 0b0001 = 1
const CAN_WRITE  = 1 << 1; // 0b0010 = 2
const CAN_DELETE = 1 << 2; // 0b0100 = 4
const CAN_ADMIN  = 1 << 3; // 0b1000 = 8
 
// Grant read + write by combining with OR
let permissions = CAN_READ | CAN_WRITE; // 0b0011 = 3
 
// Check a specific permission with AND
const hasWriteAccess = (permissions & CAN_WRITE) !== 0; // true
const hasAdminAccess = (permissions & CAN_ADMIN) !== 0;  // false
 
// Add a permission without disturbing the others
permissions |= CAN_DELETE; // now 0b0111 = 7
 
// Remove a permission without disturbing the others
permissions &= ~CAN_WRITE; // clears just the WRITE bit -> 0b0101 = 5
 
// Toggle a permission (on becomes off, off becomes on)
permissions ^= CAN_READ; // flips just the READ bit

Each permission gets its own bit position (1 << 0, 1 << 1, 1 << 2...), so combining them with | never overwrites another flag — they occupy entirely separate positions in the same integer. Checking with & isolates just that one bit; every other bit is masked out to zero, so the comparison only ever reflects the flag you asked about.

Why this matters in production: a role/permission system with a handful of booleans stored as five separate boolean columns costs five columns, five reads, and five separate comparisons every time you check "can this user do X." As a single integer, storage is one column, and checking any combination of permissions is a single bitwise comparison — no matter how many flags you add, later, without a schema migration for each one.


Feature Flags as Bits

The same pattern shows up for feature toggles — a single integer field on a user or tenant record can represent "which experimental features are enabled for this account," checked and updated exactly like the permission bits above:

enum Feature {
  DarkMode = 1 << 0,
  BetaSearch = 1 << 1,
  NewCheckout = 1 << 2,
}
 
function hasFeature(flags: number, feature: Feature): boolean {
  return (flags & feature) !== 0;
}
 
let userFlags = Feature.DarkMode | Feature.NewCheckout;
hasFeature(userFlags, Feature.BetaSearch); // false
hasFeature(userFlags, Feature.NewCheckout); // true

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...