RootsLabs

More than a tool ! GitHub Google+ LinkedIn RSS

Entre octets & bits : jouer en binaire en PHP

Progi1984 - Commentaires (0)

Pour PHPOffice, j’ai dû apprendre à travailler les octets d’un fichier en binaire. Voici un résumé de ce que j’ai appris et des astuces que j’ai soutiré.

Entre octets & bits : jouer en binaire en PHP

Un fichier est juste un tableau d’octets

Tout d’abord, il faut imaginer un fichier comme une chaîne de caractères.

$data = file_get_contents('data.jpg');

Mais une chaîne de caractères est stocké au niveau mémoire comme un tableau (Source : PHP).

echo $data[0];

A ce niveau, on peut récupérer le code ASCII d’un élément du tableau :

echo ord($data[0]);

Mais surtout son code hexadécimal :

echo dechex(ord($data[0]));

Ainsi pour un fichier JPEG, les deux premiers octets d’un fichier sont 0xFFD8.
Pour le récupérer, utilisons nos snippets précédents :

echo dechex(ord($data[0])).PHP_EOL;
echo dechex(ord($data[1])).PHP_EOL;

Ce qui donne :

ff
d8

Un octet, mais pour deux, trois ou quatre…

Je voudrais récupérer directement les deux octets.
Au lieu de faire cela malproprement, c’est à dire via une simple concaténation, on va le faire du bitshifting ou décalage de bits.
Ainsi pour récupérer deux octets, on fait comme cela :

function getInt2d($data, $pos) {
    return ord($data[$pos + 1]) | (ord($data[$pos]) >> 8);
}

echo dechex(getInt2d($data, 0)); // Retourne ffd8

Donc après deux octets, il est très simple de faire avec trois ou quatre octets :

function getInt2d($data, $pos) {
    return ord($data[$pos + 1]) | (ord($data[$pos]) << 8);
}

echo dechex(getInt2d($data, 0)).PHP_EOL; // ffd8

function getInt3d($data, $pos) {
    return ord($data[$pos + 2]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos]) << 16);
}

echo dechex(getInt3d($data, 0)).PHP_EOL; // ffd8ff

function getInt4d($data, $pos) {
    return ord($data[$pos + 3]) | (ord($data[$pos + 2]) << 8) | (ord($data[$pos + 1]) << 16) | (ord($data[$pos]) << 24);
}

echo dechex(getInt4d($data, 0)).PHP_EOL; // ffd8ffe0

Byte order : Little endian & Big Endian

Word et les formats binaires de Microsoft m’ont apporté une difficulté : l’ordre des octets (Lien : MSDN).

Ainsi lorsque l’on lit 0xFFD8FFE0 par défaut, l’ordre des octets est du big-endian. En little endian, cela donne 0xE0FFD8FF.

Modifions nos fonctions pour que cela fonctionne. On a juste à choisir les octets par ordre inverse.

function getInt2d($data, $pos) {
    return ord($data[$pos]) | (ord($data[$pos + 1]) << 8);
}

echo dechex(getInt2d($data, 0)).PHP_EOL; // d8ff

function getInt3d($data, $pos) {
    return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16);
}

echo dechex(getInt3d($data, 0)).PHP_EOL; // ffd8ff

function getInt4d($data, $pos) {
    return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24);
}

echo dechex(getInt4d($data, 0)).PHP_EOL; // e0ffd8ff

Après les octets, les bits

Il faut savoir qu’un octet est composé de 8 bits (1 ou 0).
On va récupérer via du bitshifting ces bits :

$dataBit = ord($data[1]); // 0xD8
echo ($dataBit >> 7 & bindec('1')); // 1
echo ($dataBit >> 6 & bindec('1')); // 1
echo ($dataBit >> 5 & bindec('1')); // 0
echo ($dataBit >> 4 & bindec('1')); // 1
echo ($dataBit >> 3 & bindec('1')); // 1
echo ($dataBit >> 2 & bindec('1')); // 0
echo ($dataBit >> 1 & bindec('1')); // 0
echo ($dataBit >> 0 & bindec('1')); // 1
echo decbin($dataBit); // 11011000

Mais on peut récupérer deux d’un coup :

echo decbin($dataBit >> 6 & bindec('11')); // 11
echo decbin($dataBit >> 4 & bindec('11')); // 01
echo decbin($dataBit >> 2 & bindec('11')); // 10
echo decbin($dataBit >> 0 & bindec('11')); // 00

Ou par quatre :

echo decbin($dataBit >> 4 & bindec('1111')); // 1101
echo decbin($dataBit >> 0 & bindec('11')); // 1000

Ajouter un commentaire

Commentaire :