Skip to content

Commit e636510

Browse files
committed
refactor: performance refactor for Utils
Signed-off-by: Alan Brault <alan.brault@visus.io>
1 parent 4b2dce3 commit e636510

1 file changed

Lines changed: 42 additions & 25 deletions

File tree

src/Utils.php

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,12 @@ private function __construct()
2323
{
2424
}
2525

26-
/**
27-
* Converts a single hexadecimal character to its numeric value.
28-
*
29-
* @param string $char Single hex character (0-9, a-f, A-F).
30-
*
31-
* @return int Numeric value (0-15).
32-
*/
33-
private static function parseHexCharacter(string $char): int
34-
{
35-
return match (true) {
36-
$char >= '0' && $char <= '9' => ord($char) - 48,
37-
$char >= 'a' && $char <= 'f' => ord($char) - 87,
38-
$char >= 'A' && $char <= 'F' => ord($char) - 55,
39-
default => 0,
40-
};
41-
}
42-
4326
/**
4427
* Converts a hexadecimal string to large base representation.
4528
*
29+
* Processes 8 hexadecimal characters at a time for improved performance,
30+
* reducing loop iterations by 8x compared to single-character processing.
31+
*
4632
* @param string $hexValue Hexadecimal string to convert.
4733
* @param int $base Large base for digit storage (100 million).
4834
*
@@ -51,13 +37,38 @@ private static function parseHexCharacter(string $char): int
5137
private static function convertHexToLargeBase(string $hexValue, int $base): array
5238
{
5339
$digits = [0];
40+
$len = strlen($hexValue);
41+
42+
$remainder = $len % 8;
43+
$i = 0;
44+
45+
if ($remainder > 0) {
46+
$chunk = substr($hexValue, 0, $remainder);
47+
$chunkValue = (int) hexdec($chunk);
48+
$carry = $chunkValue;
49+
$multiplier = 1 << $remainder * 4; // 16^remainder = 2^(remainder*4)
50+
51+
for ($j = 0, $jlen = count($digits); $j < $jlen; $j++) {
52+
$current = $digits[$j] * $multiplier + $carry;
53+
$digits[$j] = $current % $base;
54+
$carry = intdiv($current, $base);
55+
}
56+
57+
while ($carry > 0) {
58+
$digits[] = $carry % $base;
59+
$carry = intdiv($carry, $base);
60+
}
61+
62+
$i = $remainder;
63+
}
5464

55-
for ($i = 0, $len = strlen($hexValue); $i < $len; $i++) {
56-
$hexDigit = self::parseHexCharacter($hexValue[$i]);
57-
$carry = $hexDigit;
65+
for (; $i < $len; $i += 8) {
66+
$chunk = substr($hexValue, $i, 8);
67+
$chunkValue = (int) hexdec($chunk);
68+
$carry = $chunkValue;
5869

5970
for ($j = 0, $jlen = count($digits); $j < $jlen; $j++) {
60-
$current = $digits[$j] * 16 + $carry;
71+
$current = $digits[$j] * 4294967296 + $carry; // 16^8 = 2^32 = 4294967296
6172
$digits[$j] = $current % $base;
6273
$carry = intdiv($current, $base);
6374
}
@@ -108,15 +119,17 @@ private static function convertLargeBaseToBase36(array $digits, int $base): stri
108119
* Converts a hexadecimal string to base36 encoding.
109120
*
110121
* This function performs arbitrary precision base conversion without requiring
111-
* the GMP extension. It uses a large intermediate base (100 million) for efficient
112-
* arithmetic operations on large numbers.
122+
* the GMP extension. For small values (≤14 hex chars), it uses the native
123+
* base_convert() function for optimal performance. For larger values, it uses
124+
* a large intermediate base (100 million) for efficient arithmetic operations.
113125
*
114126
* Base36 encoding uses digits 0-9 and lowercase letters a-z (36 characters total),
115127
* producing shorter strings than hexadecimal while remaining URL-safe.
116128
*
117129
* Algorithm:
118-
* 1. Convert hex string to internal representation using base 100M
119-
* 2. Convert internal representation to base36 by repeated division
130+
* 1. Fast path: Use base_convert() for small values (≤14 hex chars / 56 bits)
131+
* 2. Large values: Convert hex string to internal representation using base 100M
132+
* 3. Convert internal representation to base36 by repeated division
120133
*
121134
* @param string $hexValue Hexadecimal string to convert (case-insensitive).
122135
*
@@ -128,6 +141,10 @@ public static function hexToBase36(string $hexValue): string
128141
return '0';
129142
}
130143

144+
if (strlen($hexValue) <= 14) {
145+
return base_convert($hexValue, 16, 36);
146+
}
147+
131148
$base = 100_000_000;
132149
$digits = self::convertHexToLargeBase($hexValue, $base);
133150

0 commit comments

Comments
 (0)