X Xerobit

Bitmask and Bitwise Operations — Flags, Permissions, and Bit Manipulation

Bitmasks store multiple boolean flags in a single integer using bitwise AND, OR, and XOR. Learn how to set, clear, and check bits for file permissions, feature flags, and game...

Mian Ali Khalid · · 4 min read
Use the tool
Number Base Converter
Convert between binary, octal, decimal, hexadecimal, and text (UTF-8). Handles arbitrary lengths. Per-byte and per-character views.
Open Number Base Converter →

A bitmask uses individual bits of an integer as independent boolean flags. One 32-bit integer holds 32 independent boolean values, with O(1) set/get/clear operations.

Convert between binary and decimal with the Number Base Converter.

Bitwise operators

// AND: both bits must be 1
0b1010 & 0b1100  // 0b1000 = 8

// OR: at least one bit is 1
0b1010 | 0b1100  // 0b1110 = 14

// XOR: exactly one bit is 1 (toggle)
0b1010 ^ 0b1100  // 0b0110 = 6

// NOT: flip all bits
~0b00001111      // 0b11110000 (as 32-bit: -16)

// Left shift: multiply by powers of 2
1 << 3           // 8 (1 × 2³)
0b0001 << 2      // 0b0100 = 4

// Right shift: divide by powers of 2
16 >> 2          // 4
0b1000 >> 1      // 0b0100 = 4

Define flags with bit shifts

// Define named flags as powers of 2:
const Permission = {
  NONE:    0,
  READ:    1 << 0,  // 0b0001 = 1
  WRITE:   1 << 1,  // 0b0010 = 2
  EXECUTE: 1 << 2,  // 0b0100 = 4
  ADMIN:   1 << 3,  // 0b1000 = 8
};

// Set flags: combine with OR
const userPerms = Permission.READ | Permission.WRITE;
// 0b0001 | 0b0010 = 0b0011 = 3

// Check a flag: AND with the flag
const canRead = (userPerms & Permission.READ) !== 0;   // true
const canExec = (userPerms & Permission.EXECUTE) !== 0; // false

// Add a flag:
const newPerms = userPerms | Permission.EXECUTE;  // 0b0111 = 7

// Remove a flag: AND with NOT of the flag
const stripped = userPerms & ~Permission.WRITE;   // 0b0001 = 1

// Toggle a flag:
const toggled = userPerms ^ Permission.WRITE;     // 0b0001 = 1 (removes WRITE)

Practical example: Unix file permissions

// Unix rwxrwxrwx permissions:
const PERM = {
  OWNER_READ:    0o400,  // 256
  OWNER_WRITE:   0o200,  // 128
  OWNER_EXEC:    0o100,  // 64
  GROUP_READ:    0o040,  // 32
  GROUP_WRITE:   0o020,  // 16
  GROUP_EXEC:    0o010,  // 8
  OTHERS_READ:   0o004,  // 4
  OTHERS_WRITE:  0o002,  // 2
  OTHERS_EXEC:   0o001,  // 1
};

// chmod 644 = rw-r--r--
const permissions = PERM.OWNER_READ | PERM.OWNER_WRITE |
                    PERM.GROUP_READ | PERM.OTHERS_READ;
// = 0o644 = 420

// Check if owner can write:
(permissions & PERM.OWNER_WRITE) !== 0;  // true

// Convert to rwxrwxrwx string:
function permsToString(p) {
  return [
    [PERM.OWNER_READ, 'r'], [PERM.OWNER_WRITE, 'w'], [PERM.OWNER_EXEC, 'x'],
    [PERM.GROUP_READ, 'r'], [PERM.GROUP_WRITE, 'w'], [PERM.GROUP_EXEC, 'x'],
    [PERM.OTHERS_READ, 'r'], [PERM.OTHERS_WRITE, 'w'], [PERM.OTHERS_EXEC, 'x'],
  ].map(([flag, ch]) => (p & flag) ? ch : '-').join('');
}
permsToString(0o644);  // "rw-r--r--"

Feature flags with bitmasks

// Store multiple feature toggles in one integer (useful in cookies/URLs):
const Features = {
  DARK_MODE:      1 << 0,  // 1
  BETA_EDITOR:    1 << 1,  // 2
  AI_SUGGESTIONS: 1 << 2,  // 4
  NEW_DASHBOARD:  1 << 3,  // 8
};

// User with dark mode + AI suggestions enabled:
const userFeatures = Features.DARK_MODE | Features.AI_SUGGESTIONS;
// = 0b0101 = 5

// Store in cookie as single integer:
document.cookie = `features=${userFeatures}`;

// Check feature:
const isDarkMode = (userFeatures & Features.DARK_MODE) !== 0;

Python bitmasks

# Define flags:
from enum import IntFlag

class Permission(IntFlag):
    NONE    = 0
    READ    = 1 << 0
    WRITE   = 1 << 1
    EXECUTE = 1 << 2
    ADMIN   = 1 << 3

# IntFlag supports all bitwise operations naturally:
user_perms = Permission.READ | Permission.WRITE
# Permission.READ|WRITE

user_perms & Permission.ADMIN  # Permission(0) — falsy
user_perms & Permission.READ   # Permission.READ — truthy

# Check which flags are set:
for perm in Permission:
    if perm in user_perms:
        print(f"{perm.name}: enabled")
# READ: enabled
# WRITE: enabled

Bit manipulation tricks

// Check if number is a power of 2:
function isPowerOfTwo(n) {
  return n > 0 && (n & (n - 1)) === 0;
}
isPowerOfTwo(64);  // true  (64 = 0b01000000)
isPowerOfTwo(48);  // false (48 = 0b00110000)

// Isolate the lowest set bit:
function lowestBit(n) {
  return n & (-n);
}
lowestBit(0b10110);  // 0b00010 = 2

// Count set bits (popcount):
function popcount(n) {
  let count = 0;
  while (n) {
    count += n & 1;
    n >>= 1;
  }
  return count;
}
popcount(0b10110);  // 3

// Round up to nearest power of 2:
function nextPowerOfTwo(n) {
  n--;
  n |= n >> 1;
  n |= n >> 2;
  n |= n >> 4;
  n |= n >> 8;
  n |= n >> 16;
  return n + 1;
}
nextPowerOfTwo(100);  // 128

RGB color packing

// Pack R, G, B into a 32-bit integer:
function packRGB(r, g, b) {
  return (r << 16) | (g << 8) | b;
}

function unpackRGB(packed) {
  return {
    r: (packed >> 16) & 0xFF,
    g: (packed >> 8) & 0xFF,
    b: packed & 0xFF,
  };
}

const color = packRGB(255, 128, 0);  // 0xFF8000 = orange
unpackRGB(color);  // { r: 255, g: 128, b: 0 }

Related posts

Related tool

Number Base Converter

Convert between binary, octal, decimal, hexadecimal, and text (UTF-8). Handles arbitrary lengths. Per-byte and per-character views.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.