xref: /dokuwiki/inc/Ip32.php (revision 2f70db90a6d48ecf1f6af51603dba5a630662d2a)
1<?php
2
3/**
4 * DokuWiki 32bit shims for IP address functions.
5 *
6 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 */
8
9namespace dokuwiki;
10
11class Ip32
12{
13    public static $b32 = '4294967296';
14    /**
15     * slow and ugly bitwise_and for 32bit arch
16     * @param $u64 unsigned 64bit integer as string
17     *            likely from ipv6UpperLowerOn32
18     * @param $pow 0-64 power of 2 for bitmask
19     */
20    public static function bitmask64On32(string $u64, int $pow) : string {
21        $upper = bcdiv($u64, Ip32::$b32, 0);
22        $lower = bcmod($u64, Ip32::$b32);
23        // str of len 32 all 0s and 1s
24        $upper_bin = Ip32::decimalToBinary32($upper);
25        $lower_bin = Ip32::decimalToBinary32($lower);
26        $bin = $upper_bin . $lower_bin;
27
28        $mask = Ip32::makeBitmaskOn32(64-$pow);
29
30        // most right is lowest bit
31        $res='0';
32        for ($i=0; $i<64; $i++){
33            if (bcmul($bin[$i], $mask[$i]) == 1) {
34                $res = bcadd($res, bcpow(2, 63-$i));
35            }
36        }
37        return $res;
38    }
39
40
41    /**
42     * modeling bitshift like  ~0 << $pow for 32-bit arch
43     * @param pow power of 2 for mask
44     * @return 64-char string of 1 and 0s
45     * pow=1
46     * 1111111111111111111111111111111111111111111111111111111111111110
47     * pow=63
48     * 1000000000000000000000000000000000000000000000000000000000000000
49     * pow=64
50     * 0000000000000000000000000000000000000000000000000000000000000000
51     */
52    private static function makeBitmaskOn32(int $pow) : string {
53        $pow = $pow < 0 ? 64 - $pow : $pow;
54        $mask = sprintf("%064d",0);
55        for ($i=0; $i<64; $i++) {
56            if ($i >= $pow) {
57                $mask[63 - $i] = '1';
58            }
59        }
60        return $mask;
61    }
62
63    /**
64     * conversion of inet_pton ipv6 into 64-bit upper and lower
65     * bcmath version for 32-bit architecture
66     * w/o no unpack('J') - unsigned long long (always 64 bit, big endian byte order)
67     *
68     * results match unpack('Jupper/Jlower', $binary)
69     *
70     * @param string $binary inet_pton's ipv6 16 element binary
71     *
72     * @return string[] upper 64 and lower 64 for ipToNumber
73     */
74    public static function ipv6UpperLowerOn32(string $binary) {
75       // unpack into four 32-bit unsigned ints to recombine as 2 64-bit
76       $parts = unpack('N4', $binary);
77       $upper = Ip32::partsTo64($parts[1], $parts[2]);
78       $lower = Ip32::partsTo64($parts[3], $parts[4]);
79       return ['upper' => $upper, 'lower' => $lower];
80    }
81
82    private static function partsTo64(string $high, string $low) : string {
83        // signed to unsigned
84        $high = $high<0 ? bcadd($high, Ip32::$b32) : $high;
85        $low  = $low <0 ? bcadd($low , Ip32::$b32) : $low;
86
87        return bcadd(bcmul($high, Ip32::$b32), $low);
88    }
89
90    /**
91     * Convert a decimal number to 32-bit binary string using bcmath
92     * Handles large numbers that exceed PHP_INT_MAX on 32-bit systems
93     *
94     * @param string $decimal The decimal number as string
95     * @return string 32-bit binary representation
96     */
97    private static function decimalToBinary32(string $decimal) : string {
98        if (bccomp($decimal, '0') == 0) {
99            return str_repeat('0', 32);
100        }
101        $binary = '';
102        $num = $decimal;
103        for ($i = 31; $i >= 0; $i--) {
104            $power = bcpow('2', (string)$i);
105            if (bccomp($num, $power) >= 0) {
106                $binary .= '1';
107                $num = bcsub($num, $power);
108            } else {
109                $binary .= '0';
110            }
111        }
112        return $binary;
113    }
114 }
115