Entre octets & bits : jouer en binaire en PHP
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é.
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