• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mlocati / ip-lib / 17973328177

24 Sep 2025 10:05AM UTC coverage: 98.746% (+0.07%) from 98.675%
17973328177

Pull #106

github

web-flow
Merge 4896f88fb into 052df264c
Pull Request #106: Let getAddressAtOffset() accept numeric strings

134 of 135 new or added lines in 2 files covered. (99.26%)

4 existing lines in 1 file now uncovered.

1102 of 1116 relevant lines covered (98.75%)

205.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.0
/src/Service/NumberInChunks.php
1
<?php
2

3
namespace IPLib\Service;
4

5
use InvalidArgumentException;
6

7
/**
8
 * @internal
9
 * @readonly
10
 */
11
class NumberInChunks
12
{
13
    const CHUNKSIZE_BYTES = 8;
14

15
    const CHUNKSIZE_WORDS = 16;
16

17
    /**
18
     * @var bool
19
     */
20
    public $negative;
21

22
    /**
23
     * @var int[]
24
     */
25
    public $chunks;
26

27
    /**
28
     * @var int
29
     */
30
    public $chunkSize;
31

32
    /**
33
     * @param bool $negative
34
     * @param int[] $chunks
35
     * @param int $chunkSize
36
     */
37
    public function __construct($negative, array $chunks, $chunkSize)
38
    {
39
        $this->negative = $negative;
40✔
40
        $this->chunks = $chunks;
40✔
41
        $this->chunkSize = $chunkSize;
40✔
42
    }
40✔
43

44
    /**
45
     * @throws \InvalidArgumentException if $other has a $chunkSize that's not the same as the $chunkSize of this
46
     *
47
     * @return \IPLib\Service\NumberInChunks
48
     */
49
    public function negate()
50
    {
51
        return new self($this->chunks === array(0) ? false : !$this->negative, $this->chunks, $this->chunkSize);
13✔
52
    }
53

54
    /**
55
     * @throws \InvalidArgumentException if $other has a $chunkSize that's not the same as the $chunkSize of this
56
     *
57
     * @return \IPLib\Service\NumberInChunks
58
     */
59
    public function add(NumberInChunks $that)
60
    {
61
        if ($this->chunkSize !== $that->chunkSize) {
14✔
62
            throw new InvalidArgumentException('Incompatible chunk size');
1✔
63
        }
64
        if ($this->negative === $that->negative) {
13✔
65
            return new self($this->negative, self::addChunks($this->chunks, $that->chunks, $this->chunkSize), $this->chunkSize);
8✔
66
        }
67
        if ($that->negative) {
9✔
68
            list($negative, $chunks) = self::substractChunks($this->chunks, $that->chunks, $this->chunkSize);
8✔
69
        } else {
70
            list($negative, $chunks) = self::substractChunks($that->chunks, $this->chunks, $this->chunkSize);
8✔
71
        }
72

73
        return new self($negative, $chunks, $this->chunkSize);
9✔
74
    }
75

76
    /**
77
     * @param int $int
78
     * @param int $chunkSize
79
     *
80
     * @return \IPLib\Service\NumberInChunks
81
     */
82
    public static function fromInteger($int, $chunkSize)
83
    {
84
        if ($int === 0) {
31✔
85
            return new self(false, array(0), $chunkSize);
6✔
86
        }
87
        $negative = $int < 0;
29✔
88
        if ($negative) {
29✔
89
            $positiveInt = -$int;
16✔
90
            if (is_float($positiveInt)) { // -PHP_INT_MIN is bigger than PHP_INT_MAX
16✔
NEW
91
                return self::fromNumericString((string) $int, $chunkSize);
×
92
            }
93
            $int = $positiveInt;
16✔
94
        }
95
        $bitMask = (1 << $chunkSize) - 1;
29✔
96
        $chunks = array();
29✔
97
        while ($int !== 0) {
29✔
98
            $chunks[] = $int & $bitMask;
29✔
99
            $int >>= $chunkSize;
29✔
100
        }
101

102
        return new self($negative, array_reverse($chunks), $chunkSize);
29✔
103
    }
104

105
    /**
106
     * @param string $numericString a string normalized with BinaryMath::normalizeIntegerString()
107
     * @param int $chunkSize
108
     *
109
     * @return \IPLib\Service\NumberInChunks
110
     */
111
    public static function fromNumericString($numericString, $chunkSize)
112
    {
113
        if ($numericString === '0') {
9✔
114
            return new self(false, array(0), $chunkSize);
1✔
115
        }
116
        $negative = $numericString[0] === '-';
8✔
117
        if ($negative) {
8✔
118
            $numericString = substr($numericString, 1);
8✔
119
        }
120
        $chunks = array();
8✔
121
        while ($numericString !== '0') {
8✔
122
            $chunks[] = self::modulo($numericString, $chunkSize);
8✔
123
            $numericString = self::divide($numericString, $chunkSize);
8✔
124
        }
125

126
        return new self($negative, array_reverse($chunks), $chunkSize);
8✔
127
    }
128

129
    /**
130
     * @param string $numericString
131
     * @param int $chunkSize
132
     *
133
     * @return int
134
     */
135
    private static function modulo($numericString, $chunkSize)
136
    {
137
        $divisor = 1 << $chunkSize;
8✔
138
        $carry = 0;
8✔
139
        $len = strlen($numericString);
8✔
140
        for ($i = 0; $i < $len; $i++) {
8✔
141
            $digit = (int) $numericString[$i];
8✔
142
            $carry = ($carry * 10 + $digit) % $divisor;
8✔
143
        }
144

145
        return $carry;
8✔
146
    }
147

148
    /**
149
     * @param string $numericString
150
     * @param int $chunkSize
151
     *
152
     * @return string
153
     */
154
    private static function divide($numericString, $chunkSize)
155
    {
156
        $divisor = 1 << $chunkSize;
8✔
157
        $quotient = '';
8✔
158
        $carry = 0;
8✔
159
        $len = strlen($numericString);
8✔
160
        for ($i = 0; $i < $len; $i++) {
8✔
161
            $digit = (int) $numericString[$i];
8✔
162
            $value = $carry * 10 + $digit;
8✔
163
            $quotient .= (string) ($value >> $chunkSize);
8✔
164
            $carry = $value % $divisor;
8✔
165
        }
166

167
        return ltrim($quotient, '0') ?: '0';
8✔
168
    }
169

170
    /**
171
     * @param int[] $addend1
172
     * @param int[] $addend2
173
     * @param int $chunkSize
174
     *
175
     * @return int[]
176
     */
177
    private static function addChunks(array $addend1, array $addend2, $chunkSize)
178
    {
179
        $divisor = 1 << $chunkSize;
8✔
180
        $result = array();
8✔
181
        $carry = 0;
8✔
182
        while ($addend1 !== array() || $addend2 !== array()) {
8✔
183
            $sum = $carry + (array_pop($addend1) ?: 0) + (array_pop($addend2) ?: 0);
8✔
184
            $result[] = $sum % $divisor;
8✔
185
            $carry = $sum >> $chunkSize;
8✔
186
        }
187
        if ($carry !== 0) {
8✔
188
            $result[] = $carry;
2✔
189
        }
190

191
        return array_reverse($result);
8✔
192
    }
193

194
    /**
195
     * @param int[] $minuend
196
     * @param int[] $subtrahend
197
     * @param int $chunkSize
198
     *
199
     * @return array
200
     */
201
    private static function substractChunks(array $minuend, array $subtrahend, $chunkSize)
202
    {
203
        $minuendCount = count($minuend);
9✔
204
        $subtrahendCount = count($subtrahend);
9✔
205
        if ($minuendCount > $subtrahendCount) {
9✔
206
            $count = $minuendCount;
1✔
207
            $negative = false;
1✔
208
        } elseif ($minuendCount < $subtrahendCount) {
9✔
209
            $count = $subtrahendCount;
1✔
210
            $negative = true;
1✔
211
        } else {
212
            $count = $minuendCount;
9✔
213
            $negative = false;
9✔
214
            for ($i = 0; $i < $count; $i++) {
9✔
215
                $delta = $minuend[$i] - $subtrahend[$i];
9✔
216
                if ($delta === 0) {
9✔
217
                    continue;
2✔
218
                }
219
                if ($delta < 0) {
8✔
220
                    $negative = true;
8✔
221
                }
222
                break;
8✔
223
            }
224
        }
225
        if ($negative) {
9✔
226
            list($minuend, $subtrahend) = array($subtrahend, $minuend);
8✔
227
        }
228
        $subtrahend = array_pad($subtrahend, -$count, 0);
9✔
229
        $borrowValue = 1 << $chunkSize;
9✔
230
        $result = array();
9✔
231
        $borrow = 0;
9✔
232
        for ($i = $count - 1; $i >= 0; $i--) {
9✔
233
            $value = $minuend[$i] - $subtrahend[$i] - $borrow;
9✔
234
            if ($value < 0) {
9✔
235
                $value += $borrowValue;
1✔
236
                $borrow = 1;
1✔
237
            } else {
238
                $borrow = 0;
9✔
239
            }
240
            $result[] = $value;
9✔
241
        }
242
        while (isset($result[1])) {
9✔
243
            $value = array_pop($result);
3✔
244
            if ($value !== 0) {
3✔
245
                $result[] = $value;
3✔
246
                break;
3✔
247
            }
248
        }
249

250
        return array($negative, array_reverse($result));
9✔
251
    }
252
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc