Как перевести с десятичной на битовую маску?

У меня есть система 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
Другие вопросы по тегам