PHP Utiliser bcmath pour lire / écrire un long binaire sur un système 32 bits


Exemple

Sur les systèmes 32 bits, les entiers supérieurs à 0x7FFFFFFF ne peuvent pas être stockés primitivement, tandis que les entiers compris entre 0x0000000080000000 et 0x7FFFFFFFFFFFFFFF peuvent être stockés primitivement sur les systèmes 64 bits, mais pas sur les systèmes 32 bits ( signed long long ). Cependant, les systèmes 64 bits et de nombreux autres langages prenant en charge le stockage d'entiers signed long long , il est parfois nécessaire de stocker cette plage d'entiers en valeur exacte. Il existe plusieurs manières de le faire, telles que la création d’un tableau à deux chiffres ou la conversion du nombre entier sous sa forme décimale. Cela présente plusieurs avantages, tels que la facilité de présentation à l'utilisateur et la possibilité de le manipuler directement avec bcmath.

Les méthodes pack / unpack peuvent être utilisées pour convertir entre les octets binaires et la forme décimale des nombres (tous deux de type string , mais l'un est binaire et l'autre ASCII), mais ils essaieront toujours de convertir la chaîne ASCII en 32 bits. int sur les systèmes 32 bits. L'extrait suivant fournit une alternative:

/** Use pack("J") or pack("p") for 64-bit systems */
function writeLong(string $ascii) : string {
    if(bccomp($ascii, "0") === -1) { // if $ascii < 0
        // 18446744073709551616 is equal to (1 << 64)
        // remember to add the quotes, or the number will be parsed as a float literal
        $ascii = bcadd($ascii, "18446744073709551616");
    }

    // "n" is big-endian 16-bit unsigned short. Use "v" for small-endian.
    return pack("n", bcmod(bcdiv($ascii, "281474976710656"), "65536")) .
        pack("n", bcmod(bcdiv($ascii, "4294967296"), "65536")) .
        pack("n", bcdiv($ascii, "65536"), "65536")) .
        pack("n", bcmod($ascii, "65536"));
}

function readLong(string $binary) : string {
    $result = "0";
    $result = bcadd($result, unpack("n", substr($binary, 0, 2)));
    $result = bcmul($result, "65536");
    $result = bcadd($result, unpack("n", substr($binary, 2, 2)));
    $result = bcmul($result, "65536");
    $result = bcadd($result, unpack("n", substr($binary, 4, 2)));
    $result = bcmul($result, "65536");
    $result = bcadd($result, unpack("n", substr($binary, 6, 2)));

    // if $binary is a signed long long
    // 9223372036854775808 is equal to (1 << 63) (note that this expression actually does not work even on 64-bit systems)
    if(bccomp($result, "9223372036854775808") !== -1) { // if $result >= 9223372036854775807
        $result = bcsub($result, "18446744073709551616"); // $result -= (1 << 64)
    }
    return $result;
}