ビット演算について - ビットマスクによる権限管理をJavaScriptで実装する
ビット演算が必要な場面があり、調べました。学んだことを整理します。
ビット演算とは?
ビット演算とは、ビット(0と1)を直接操作する計算方法です。
通常の四則演算(+
-
*
/
)とは異なり、
AND(&
)、OR(|
)、XOR(^
)、NOT(~
)、シフト(<<
>>
) を用いて計算を行います。
ビット演算を用いると、複数の状態を1つの数値で管理することができるので、以下のような場面で役立ちます。
- フラグの管理(権限など)
- 曜日によるスケジュール管理(タスク実行日を月、水、金に設定する、など)
- ゲームの状態管理(アイテム所持の状態管理など)
ビット演算子
ビットの演算には以下の演算子を用います。
演算子 | 記号 | 説明 |
---|---|---|
AND | & |
両方のビットが1 の時1
|
OR | | |
どちらかのビットが1の時、1 |
XOR | ^ |
ビットが異なる時1、同じなら0 |
NOT |
! 、~
|
全てのビットを反転(0 →1 , 1 →0 ) |
左シフト | << |
ビットを左にn回ずらす |
右シフト(符号付き) | >> |
ビットを右にn回ずらす(符号を保持) |
右シフト(ゼロ埋め) | >>> |
ビットを右にn回ずらし、符号なしにする |
&
)演算
AND(両方のビットが1
の場合のみ1
になる
console.log(5 & 3)
// 5 : 101
// 3 : 011
→ 1 : 001
|
)演算
OR(どちらかのビットが1
なら1
になる
console.log(5 | 3)
// 5 : 101
// 3 : 011
→ 7 : 111
^
)演算
XOR(ビットが異なる場合1
、同じなら0
console.log(5 ^ 3)
// 5 : 101
// 3 : 011
→ 6 : 110
~
)演算
NOT(ビットの反転をする。ビットが異なる場合1
、同じなら0
console.log(~5)
// 5 : 00000101
→ -6 : 11111010 *2の補数
<<
)
左シフト(ビットを左に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