Как выполнять бинарные операции над полубайтом/тетрадой в php?

Итак, у меня есть числовые значения от 0 до 15, поэтому я сохранил их в шестнадцатеричных кодах (от 0 до f). Теперь у меня есть строка данных, содержащая значения шестнадцатеричного кода моих кусочков.

Данные выглядят так:

a0fc3d78270db962e4ba525cf3acd

Каков точный/элегантный/быстрый способ выполнения двоичного xor для двух кусочков и каков будет самый быстрый способ выполнения двоичного кода не для кусочка?

Сейчас я имею в виду сначала преобразовать фрагменты в полные байты:

$nibble = "c";
$numeric = ord($nibble);
$byte = ($numeric<58)?chr($numeric-48):chr($numeric-55);

Затем выполните желаемую операцию (исключающее или нет) над этими байтами и снова преобразуйте полученное значение в полубайт.

$byte1 = chr(7); $byte2=chr(12);
$xor_val = $byte1 ^ $byte2;
$numeric = ord($xor_val);
$nibble = ($numeric<58)?chr($numeric+48):chr($numeric+55);

Проблема с этим подходом заключается в том, что если я применяю операцию not (~) к байту, она также меняет местами первые 4 бита (следовательно, добавляя 1111 к левой части полубайта), и мне приходится входить в дополнительную сложность вычитая 240 из значения ord() результата, прежде чем я снова преобразую его в откусывание с помощью кода, представленного выше. Это не только усложняет будущие обновления кода, но и затрудняет интерпретацию функциональности кода в будущем.

Каков наилучший/точный способ выполнения побитового xor, а не на откусках и получения результирующего значения в виде шестнадцатеричного кода (строки)?

Примеры:

'3' xnor 'a' = '6'  
'c' xnor '5' = '6'  
'b' xnor '8' = 'c'

person Youstay Igo    schedule 31.07.2018    source источник


Ответы (1)


Использование двоичной операции И для выбора только соответствующих битов:

$nibble1 = hexdec('3');
$nibble2 = hexdec('a');

// nibble1 xnor nibble2
$r = ~($nibble1 ^ $nibble2) & 0x0F;

echo dechex($r); // '6'

Это называется «маскированием» битов, где 0x0F называется «маской».

Без маскирования результатом операции ~(0x0b ^ 0x08) будет ffffffffffffffffc из-за того, что PHP представляет целые числа как 64-битные длинные.

Теперь, когда мы применяем маску, происходит следующее (я пропустил 4 старших байта для облегчения визуализации):

11111111 11111111 11111111 11111100 (ffffffffc)
00000000 00000000 00000000 00001111 (00000000f)
----------------------------------- & (binary AND)
00000000 00000000 00000000 00001100 (00000000c)

Мы «выбираем» только нижний последний полубайт.

Чтобы замаскировать верхний полубайт, используйте 0xF0 и (необязательно) сдвиг вправо 4.

$byte1 = hexdec('3f');
$byte2 = hexdec('a5');

$r = (~($byte1 ^ $byte2) & 0xF0) >> 4;

echo dechex($r); // Also '6'
person Pete    schedule 01.08.2018