🍣

ビット演算について - ビットマスクによる権限管理をJavaScriptで実装する

2025/02/11に公開

ビット演算が必要な場面があり、調べました。学んだことを整理します。

ビット演算とは?

ビット演算とは、ビット(0と1)を直接操作する計算方法です。
通常の四則演算(+ - * /)とは異なり、
AND(&)、OR(|)、XOR(^)、NOT(~)、シフト(<< >>) を用いて計算を行います。

ビット演算を用いると、複数の状態を1つの数値で管理することができるので、以下のような場面で役立ちます。

  • フラグの管理(権限など)
  • 曜日によるスケジュール管理(タスク実行日を月、水、金に設定する、など)
  • ゲームの状態管理(アイテム所持の状態管理など)

ビット演算子

ビットの演算には以下の演算子を用います。

演算子 記号 説明
AND & 両方のビットが1の時1
OR | どちらかのビットが1の時、1
XOR ^ ビットが異なる時1、同じなら0
NOT ! ~ 全てのビットを反転(01, 10)
左シフト << ビットを左にn回ずらす
右シフト(符号付き) >> ビットを右にn回ずらす(符号を保持)
右シフト(ゼロ埋め) >>> ビットを右にn回ずらし、符号なしにする

AND(&)演算

両方のビットが1の場合のみ1になる

console.log(5 & 3)
// 5 : 101
// 3 : 0111 : 001

OR(|)演算

どちらかのビットが1なら1になる

console.log(5 | 3)
// 5 : 101
// 3 : 0117 : 111

XOR(^)演算

ビットが異なる場合1、同じなら0

console.log(5 ^ 3)
// 5 : 101
// 3 : 0116 : 110

NOT(~)演算

ビットの反転をする。ビットが異なる場合1、同じなら0

console.log(~5)
// 5 : 00000101-6 : 111110102の補数

左シフト(<<)

ビットを左にn回シフトする。元の値の2^n倍になる

console.log(5 << 1)
// 5    : 00000101
// 5<<1 : 00001010 (左に1ビット移動)10

符号付き右シフト(>>)

符号を保持したまま、ビットを右にn回シフトする。

正の数の場合

console.log(5 >> 1)
// 5    : 00000101
// 5>>1 : 00000010 (右に1ビット移動)2

負の数の場合

console.log(-5 >> 1)
// -5    : 11111011 (2の補数)
// 5>>1  : 11111101 (符号を保持し、右に1ビット移動)-3

ゼロ埋め右シフト(>>>)

ビットをn回右にシフトし、空いたビットを0で埋める
*正の数の場合は>>と同じ

正の数の場合

console.log(5 >>> 1)
// 5    : 00000101
// 5>>1 : 00000010 (右に1ビット移動)2

負の数の場合

console.log(-5 >>> 1)
// -5    : 11111011 (2の補数)
// 5>>>1 : 01111101 (右に1ビット移動し、0で埋める)2147483645

権限管理をビット管理する

ビット演算を使うと、複数の権限を 1つの数値 で管理できます。 例えば、以下のような権限を持つシステムを考えてみましょう。

権限名 ビット 10進数
閲覧 (READ) 0001 1
編集 (WRITE) 0010 2
削除 (DELETE) 0100 4
管理 (ADMIN) 1000 8

例えば、「閲覧」と「編集」権限を持つユーザは
READ(1)+WRITE(2) = 3(0011)
になります。

このように、1つの数値で複数の状態を表現できます。

権限を以下のように定義し、ユーザへの権限設定、権限の確認、権限の削除を実装してみます。

const READ   = 1 << 0 // 0001 = 1
const WRITE  = 1 << 1 // 0010 = 2
const DELETE = 1 << 2 // 0100 = 4
const ADMIN  = 1 << 3 // 1000 = 8

ケース1 権限を設定

ユーザに、閲覧と編集権限を付与する場合、OR(|)を使います。

let userPermissions = READ | WRITE // 閲覧 + 編集 権限を持つ
console.log(userPermissions) // 3 (0011)

userPermissions |= DELETE
console.log(userPermissions) // 7 (0111)

ケース2 ユーザの権限確認

ユーザに権限があるかどうかは、AND(&)を使い確認できます。

const hasPermission = (user, permission) => (user & permission) !== 0

console.log(hasPermission(userPermissions, READ))  // true
console.log(hasPermission(userPermissions, WRITE)) // true
console.log(hasPermission(userPermissions, ADMIN)) // false

ケース3 権限削除

ユーザの権限を削除する場合、AND NOT(& ~)を使います。

console.log(userPermissions) // 7 (0111)

userPermissions &= ~WRITE // 編集権限(`0010`)

console.log(userPermissions) // 5(0101)
// 閲覧、削除のみ残る

まとめ

ビット演算は、ビットを直接操作することで、複数の状態を効率的に管理できることがわかりました。
複数状態がありえる権限管理や、タスクに対するスケジュール管理といった場面で役立ちそうです。

しかし、演算を見て分かる通り可読性が低く、拡張性の課題もあるため、適用範囲をしっかり考える必要があります。

Discussion