Как перевести с десятичной на битовую маску?
У меня есть система ACL, ранее созданная кем-то другим, и я пытаюсь понять, как там работает битовая маскировка. Я определил 4 константы:
const NONE = 0;
const READ = 1;
const WRITE = 2;
const UPDATE = 4;
const DELETE = 8;
Затем в БД я вижу пользователей с разрешениями, такими как 1, 2, 5, 9, 15. Я попытался преобразовать их с помощью этого инструмента, и я в результате получаю следующий результат:
0 // NONE
1 // READ
2 // WRITE
3 // UPDATE|DELETE
4 // UPDATE
5 // WRITE|DELETE
6 // WRITE|UPDATE
7 // WRITE|UPDATE|DELETE
8 // DELETE
9 // READ|DELETE
10 // READ|UPDATE
11 // READ|UPDATE|DELETE
12 // READ|WRITE
13 // READ|WRITE|DELETE
14 // READ|WRITE|UPDATE
15 // READ|WRITE|DELETE|UPDATE
Как я думаю, эта работа заключается в следующем:
Decimal Hexadecimal
3 00000011
Потому что два последних бита 1
Я предполагаю, что эти пользователи, имеющие 3
буду иметь UPDATE|DELETE
разрешения (см. таблицу выше). Это правильно? Если нет, то как правильно переводить с десятичной в битовую маску?
3 ответа
0 = NONE
это особый случай, который можно проверить простым сравнением.
Если вы хотите задать вопрос постоянно cn
со значением 2^(n-1)
установить, тогда мы делаем это с (1 = да, 0 = нет, % = по модулю):
(value / cn) % 2
Если мы хотим получить все установленные флаги, вы можете сделать это с помощью следующего псевдокода:
c := 1
while value > 0
if value % 2 = 1
// constant c is set
...
end if
value := value / 2
c := c * 2
end while
Из некоторого кода Perl я написал:
#!/usr/bin/perl
use warnings;
use strict;
# convert numeric status to string as enumerated type
sub enum_status_string($$)
{
my ($value, $aref) = @_;
my $last_index = $#$aref;
return sprintf('%u(%s)', $value, $value > $last_index ?
$aref->[$last_index] : $aref->[$value]);
}
# convert numeric status to string as bitset
sub bit_status_string($$)
{
my ($value, $aref) = @_;
my $last_index = $#$aref;
my $result = sprintf('%#x(', $value);
my $sep;
for (my ($bit, $mask) = (0, 1); $bit <= $last_index; ++$bit, $mask <<= 1) {
if ($value & $mask) {
$result .= $sep
if ($sep);
$result .= enum_status_string($bit, $aref);
$sep = '+';
}
}
return $result . ')';
}
my @ACL = (qw(NONE READ WRITE UPDATE DELETE));
for (my $i = 0; $i < 16; ++$i) {
print "$i: ", bit_status_string($i, \@ACL), "\n";
}
Выход:
0: 0()
1: 0x1(0(NONE))
2: 0x2(1(READ))
3: 0x3(0(NONE)+1(READ))
4: 0x4(2(WRITE))
5: 0x5(0(NONE)+2(WRITE))
6: 0x6(1(READ)+2(WRITE))
7: 0x7(0(NONE)+1(READ)+2(WRITE))
8: 0x8(3(UPDATE))
9: 0x9(0(NONE)+3(UPDATE))
10: 0xa(1(READ)+3(UPDATE))
11: 0xb(0(NONE)+1(READ)+3(UPDATE))
12: 0xc(2(WRITE)+3(UPDATE))
13: 0xd(0(NONE)+2(WRITE)+3(UPDATE))
14: 0xe(1(READ)+2(WRITE)+3(UPDATE))
15: 0xf(0(NONE)+1(READ)+2(WRITE)+3(UPDATE))
Я знаю, что этому уже много лет, но меня беспокоило отсутствие решения.
Если бы мне пришлось оценить это:
0 // NONE
1 // READ
2 // WRITE
X 3 // UPDATE|DELETE
4 // UPDATE
X 5 // WRITE|DELETE
6 // WRITE|UPDATE
X 7 // WRITE|UPDATE|DELETE
8 // DELETE
9 // READ|DELETE
X 10 // READ|UPDATE
X 11 // READ|UPDATE|DELETE
X 12 // READ|WRITE
X 13 // READ|WRITE|DELETE
X 14 // READ|WRITE|UPDATE
15 // READ|WRITE|DELETE|UPDATE
Я не совсем понимаю, как разрешения «приходят к такому результату». Единственное, о чем я могу думать, это то, что «0» рассматривается как одно из значений, что приводит к путанице значений, тогда как на самом деле это отсутствие каких-либо включенных значений, что фактически означает отсутствие доступа.
const NONE = 0; const READ = 1; const WRITE = 2; const UPDATE = 4; const DELETE = 8;
Вместо этого думайте о константах, кратных 2, как о битовых переключателях, каждый из которых может быть независимо включен(1)/отключен(0), каждый из которых умножается с использованием следующих позиционных значений, сложенных вместе:
8421
Примеры:
0000 — это 0+0+0+0 или 0: нет доступа.
0011 — это 0+0+2+1 или 3: ЧТЕНИЕ/ЗАПИСЬ.
0101 — 0+4+0+1 или 5: ЧТЕНИЕ/ОБНОВЛЕНИЕ.
Я думаю, что эта работа выглядит следующим образом:
Decimal Hexadecimal 3 00000011
Здесь путаются Hex и Binary. На самом деле это:
Decimal Hex Binary
3 03 00000011
Учитывая это, полный список можно было бы перевести так:
_______________________________
| ______________________ |
|| _____________ | |
||| ____ | | |
|||| | | | |
Value 8421 READ WRITE UPDATE DELETE Effect
----- ---- ------ ------ ------ ------ ------
0 0000 No Access
1 0001 READ Read-Only (view)
2 0010 WRITE Write-Only (dropbox-like: add new, no other access)
3 0011 READ WRITE Read/Write (view exiting; add new)
4 0100 UPDATE Update-Only (update existing, difficult to imagine an application without read)
5 0101 READ UPDATE Read/Update (view, update existing)
6 0110 WRITE UPDATE Write/Update (add new; update existing)
7 0111 READ WRITE UPDATE Read/Write/Update
8 1000 DELETE Delete-Only (difficult to imagine an application without read)
9 1001 READ DELETE Read/Delete
10 1010 WRITE DELETE Write/Delete
11 1011 READ WRITE DELETE Read/Write/Delete
12 1100 UPDATE DELETE Update/Delete
13 1101 READ UPDATE DELETE Read/Update/Delete
14 1110 WRITE UPDATE DELETE Write/Update/Delete
15 1111 READ WRITE UPDATE DELETE Full-Access