forked from muglug/psl
-
Notifications
You must be signed in to change notification settings - Fork 1
/
from_base.php
66 lines (59 loc) · 1.84 KB
/
from_base.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
declare(strict_types=1);
namespace Psl\Math;
use Psl;
use Psl\Str\Byte;
/**
* Converts the given string in the given base to an int, assuming letters a-z
* are used for digits when `$from_base` > 10.
*
* Example:
*
* Math\from_base('10', 2)
* => Int(2)
*
* Math\from_base('ff', 15)
* => Int(255)
*
* @param non-empty-string $number
*
* @pure
*
* @throws Psl\Exception\InvariantViolationException If $number is empty, $from_base is outside the [2, 36] range,
* or $number is invalid.
*/
function from_base(string $number, int $from_base): int
{
Psl\invariant('' !== $number, 'Unexpected empty string, expected number in base %d', $from_base);
Psl\invariant(
$from_base >= 2 && $from_base <= 36,
'Expected $from_base to be between 2 and 36, got %d',
$from_base
);
/** @psalm-suppress MissingThrowsDocblock */
$limit = div(INT64_MAX, $from_base);
$result = 0;
foreach (Byte\chunk($number) as $digit) {
$oval = Byte\ord($digit);
// Branches sorted by guesstimated frequency of use. */
if (/* '0' - '9' */ $oval <= 57 && $oval >= 48) {
$dval = $oval - 48;
} elseif (/* 'a' - 'z' */ $oval >= 97 && $oval <= 122) {
$dval = $oval - 87;
} elseif (/* 'A' - 'Z' */ $oval >= 65 && $oval <= 90) {
$dval = $oval - 55;
} else {
$dval = 99;
}
Psl\invariant($dval < $from_base, 'Invalid digit %s in base %d', $digit, $from_base);
$oldval = $result;
$result = $from_base * $result + $dval;
Psl\invariant(
$oldval <= $limit && $result >= $oldval,
'Unexpected integer overflow parsing %s from base %d',
$number,
$from_base
);
}
return $result;
}