javiniar.leonard javiniar.leonard - 25 days ago 7
PHP Question

Decoding a hex string into 4 pieces that maps to a binary map of values

I am trying to decode a supposedly hex string. In MS SQL Server (11.0.2100) the data has the type of

char(8)
.

In the manuals there was no clear way of decoding the data but it documents what it contains:


Given a hex string ie.
0001003F
with a length of 4. The lower byte
is on the right, the higher byte is on the left. For each of the 4
'
bytes
' a reference table that maps a 'bit' to a certain truthy
value was given. A bit order is also given having the bit 0 or the bit
in right most being the 1st bit, ..., etc.


The table looks like this:

1st 'byte':

|Bit Order | Description | 1 | 0 | trigger |
|-----------|---------------|-------------------|-------------------|---------------|
|BIT0 | state foo | state foo is ON | State foo is OFF | high level |
|BIT1 | state bar | in state bar | not in state bar | high level |
| ...
|BIT7 | state bazz | in state bazz | not in state bazz | high level |


(3 more tables follows for the next 3 other 'byte's ..., each of the 4 'byte's supposedly has 8 equal number of 'bits')

I thought the way of decoding this data is to split the hex string into 4 parts and convert them into a binary string width a fixed with of 8.

In
PHP
, taken the example hex '
0001003F
', the first byte was '
3F
', having the converted to binary,
0011 1111
(space for clarity). Then, inferred that the value for the first byte was:

'state foo is on', 'in state bar', ..., 'not in state bazz'
.

I also tried to do:
hex2bin("0001003F")
but it outputs
strin(4) " # "
.

Is this the correct way of decoding this data?

(I beg your pardon if the tags are incorrect.)

Answer

Since 4 bytes fit into the storage for integer type on almost all platforms (32-bit and higher), you can convert the hex string to integer, then use the bitwise operators to check if specific bit is set:

$hex_str = '0001003F';
$flags = base_convert($hex_str, 16, 10);

foreach (range(0, 31) as $bit) {
  printf("Bit %d: %d\n", $bit, (bool) ($flags & (1 << $bit)));
}

Output

Bit 0: 1
Bit 1: 1
Bit 2: 1
Bit 3: 1
Bit 4: 1
Bit 5: 1
Bit 6: 0
...
Bit 15: 0
Bit 16: 1
Bit 17: 0
...
Bit 31: 0

If bit $bit is set (1), then the state corresponding to this bit is on.

The code converts the hex string $hex_str to an integer $flags with the help of base_convert function. The loop iterates bit numbers in range [0;31] (starting from the least significant bit). The (1 << $bit) expression is the value 1 shifted to the left by $bit bits. Thus, if bit number $bit is set, then the result of the bitwise AND operation is a non-zero integer. The result is cast to boolean type to produce 1, if the result is non-zero, and 0 otherwise.

It is easy to see that you can test a number of bits with a single bitwise AND operation, e.g.:

// Check if at least one of three bits is set, 3rd, 10th, or 11th
$mask = (1 << 3) | (1 << 10) | (1 << 11);
if ($flags & $mask)
  printf("At least one of the bits from mask 0x%x is set\n", $mask);

Output

At least one of the bits from mask 0xc08 is set